home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Games / Xconq 7.1.0 / src / xconq-7.1.0 / x11 / xmap.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-07  |  77.9 KB  |  3,020 lines  |  [TEXT/R*ch]

  1. /* Map handling for the X11 interface to Xconq.
  2.    Copyright (C) 1987, 1988, 1989, 1991, 1992, 1993, 1994, 1995, 1996
  3.    Stanley T. Shebs.
  4.  
  5. Xconq is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2, or (at your option)
  8. any later version.  See the file COPYING.  */
  9.  
  10. /* This file contains the high-level and general handling for map
  11.    windows. */
  12.  
  13. #include "conq.h"
  14. extern int num_features PARAMS ((void));
  15. #include "xconq.h"
  16. void update_unit_controls PARAMS ((Side *side, Map *map));
  17.  
  18. static void create_map_controls PARAMS ((Side *side, Map *map));
  19.  
  20. static void interp_key_command PARAMS ((Side *side, Map *map));
  21.  
  22. static void handle_map_click PARAMS ((Side *side, Map *map, int sx, int sy));
  23.  
  24. static void help_unit_type PARAMS ((Side *side, Map *map));
  25.  
  26. static void help_terrain_type PARAMS ((Side *side, Map *map));
  27.  
  28. static void mode_callback PARAMS ((Widget w, XtPointer client_data,
  29.                   XtPointer call_data));
  30. static void command_callback PARAMS ((Widget w, XtPointer client_data,
  31.                      XtPointer call_data));
  32. static void view_flag_callback PARAMS ((Widget w, XtPointer client_data,
  33.                        XtPointer call_data));
  34. static void zoom_callback PARAMS ((Widget w, XtPointer client_data,
  35.                   XtPointer call_data));
  36. static void map_help_callback PARAMS ((Widget w, XtPointer client_data,
  37.                       XtPointer call_data));
  38. static void unit_type_list_callback PARAMS ((Widget w, XtPointer client_data,
  39.                         XtPointer call_data));
  40. static void panner_callback PARAMS ((Widget w, XtPointer client_data,
  41.                     XtPointer call_data));
  42. static void porthole_callback PARAMS ((Widget w, XtPointer client_data,
  43.                       XtPointer call_data));
  44.  
  45. static void panner_resize_handler PARAMS ((Widget w, XtPointer client_data,
  46.                       XEvent *event, Boolean *cont));
  47.  
  48. static void MA_keypress PARAMS ((Widget w, XEvent *event, String *params,
  49.                 Cardinal *num_params));
  50. static void MA_mapexpose PARAMS ((Widget w, XEvent *event, String *params,
  51.                  Cardinal *num_params));
  52. static void MA_setcoord PARAMS ((Widget w, XEvent *event, String *params,
  53.                 Cardinal *num_params));
  54. static void MA_movelook PARAMS ((Widget w, XEvent *event, String *params,
  55.                 Cardinal *num_params));
  56. static void MA_moveunit PARAMS ((Widget w, XEvent *event, String *params,
  57.                 Cardinal *num_params));
  58. static void MA_distance PARAMS ((Widget w, XEvent *event, String *params,
  59.                 Cardinal *num_params));
  60. static void MA_motionsetcoord PARAMS ((Widget w, XEvent *event, String *params,
  61.                       Cardinal *num_params));
  62. static void MA_toolaction PARAMS ((Widget w, XEvent *event, String *params,
  63.                   Cardinal *num_params));
  64. static void MA_toolselection PARAMS ((Widget w, XEvent *event, String *params,
  65.                      Cardinal *num_params));
  66. static void MA_movelook_ul PARAMS ((Widget wdgt, XEvent *event, String *params,
  67.                    Cardinal *num_params));
  68. static void MA_movelook_l  PARAMS ((Widget wdgt, XEvent *event, String *params,
  69.                    Cardinal *num_params));
  70. static void MA_movelook_dl PARAMS ((Widget wdgt, XEvent *event, String *params,
  71.                    Cardinal *num_params));
  72. static void MA_movelook_ur PARAMS ((Widget wdgt, XEvent *event, String *params,
  73.                    Cardinal *num_params));
  74. static void MA_movelook_r  PARAMS ((Widget wdgt, XEvent *event, String *params,
  75.                    Cardinal *num_params));
  76. static void MA_movelook_dr PARAMS ((Widget wdgt, XEvent *event, String *params,
  77.                    Cardinal *num_params));
  78. static void MA_center PARAMS ((Widget wdgt, XEvent *event, String *params,
  79.                   Cardinal *num_params));
  80. #if 0
  81. static void MA_message PARAMS ((Widget wdgt, XEvent *event, String *params,
  82.                    Cardinal *num_params));
  83. static void SetMessageArea PARAMS ((Widget msgarea, char *msg));
  84. #endif
  85.  
  86. static void movelook_one_cell PARAMS ((Widget w, int xdelta, int ydelta));
  87.  
  88. static void popup_ctrlpanel PARAMS ((Side *side, Map *map));
  89. static void popdown_ctrlpanel PARAMS ((Side *side, Map *map));
  90. static void create_ctrlpanel PARAMS ((Side *side, Map *map));
  91. static void ctrlpanel_cancel_callback PARAMS ((Widget w, XtPointer client_data,
  92.                           XtPointer call_data));
  93. static void ctrlpanel_revert_callback PARAMS ((Widget w, XtPointer client_data,
  94.                           XtPointer call_data));
  95. static void ctrlpanel_apply_callback PARAMS ((Widget w, XtPointer client_data,
  96.                          XtPointer call_data));
  97. static void ctrlpanel_done_callback PARAMS ((Widget w, XtPointer client_data,
  98.                         XtPointer call_data));
  99.  
  100. XtActionsRec map_actions_table[] = {
  101.     { "keypress", MA_keypress },
  102.     { "redraw", MA_mapexpose },
  103.     { "select", MA_setcoord },
  104.     { "movelook", MA_movelook },
  105.     { "moveunit", MA_moveunit },
  106.     { "distance", MA_distance },
  107.     { "motionselect", MA_motionsetcoord },
  108.     { "toolaction",  MA_toolaction },
  109.     { "toolselection", MA_toolselection },
  110.     { "movelook_ul", MA_movelook_ul },
  111.     { "movelook_l", MA_movelook_l },
  112.     { "movelook_dl", MA_movelook_dl },
  113.     { "movelook_ur", MA_movelook_ur },
  114.     { "movelook_r", MA_movelook_r },
  115.     { "movelook_dr", MA_movelook_dr },
  116.     { "center",    MA_center },
  117. };
  118.  
  119. void
  120. add_map_actions()
  121. {
  122.     extern XtAppContext thisapp;
  123.  
  124.     XtAppAddActions(thisapp, map_actions_table, XtNumber(map_actions_table));
  125. }
  126.  
  127. /* Given the total width and height of a map window, calculate the sizes
  128.    of the different subareas. */
  129.  
  130. static void
  131. subdivide_map(side, map)
  132. Side *side;
  133. Map *map;
  134. {
  135.     /* Subdivide the window into right and left halves. */
  136.     if (map->leftfrac == 0)
  137.       map->leftfrac = 80;
  138.     map->leftw = (map->leftfrac * map->totalw) / 100 ;
  139.     /* Subdivide the left half. */
  140.     map->toph = max(100, (20 * map->totalh / 100));
  141.     map->pxw = map->leftw - 40 - 2 - 2;  /* this is dubious... */
  142.     /* (should be able to tweak this) */
  143.     map->infoh = 75;
  144.     map->pxh = map->totalh - map->toph - map->infoh - 4;
  145.     /* Subdivide the list area into upper and lower lists, plus panner
  146.        space. */
  147.     map->list1w = map->totalw - map->leftw;
  148.     /* All of these have the same width. */
  149.     map->list2w = map->panw = map->list1w;
  150.     /* Compute the height of the panner by scaling by aspect ratio of
  151.        the area. */
  152.     map->panh = (area.height * map->panw) / area.width;
  153.     if (map->panh > (map->totalh * 30) / 100) {
  154.     /* Panner subarea is too big; shrink the width to fit the
  155.        available height. */
  156.     map->panh = (map->totalh * 30) / 100;
  157.     map->panw = (area.width * map->panh) / area.height;
  158.     }
  159.     /* Inset the panner a bit. */
  160.     map->panw -= 8;  map->panh -= 8;
  161.     /* Compute the side list height to be enough to show all of them. */
  162.     map->list1h = numsides * map->sidespacing;
  163.     /* ...but don't let it use more than 60% of available height. */
  164.     map->list1h = min(map->list1h, (map->totalh * 60) / 100);
  165.     /* Middle list only gets the leftovers. */
  166.     map->list2h = map->totalh - map->list1h - map->panh;
  167. }
  168.  
  169. /* Create a generic map object. */
  170.  
  171.  
  172. #if 0
  173. XtTranslations saved_translations;
  174. #endif
  175.  
  176. Map *
  177. create_map(side, power, geospec)
  178. Side *side;
  179. int power;
  180. char *geospec;
  181. {
  182.     int x, y, w, h, m, i, sx, sy, scale;
  183.     int fh = side->ui->fh;
  184.     long flags;
  185.     Map *map;
  186.     Pixmap pic;
  187.     char wbuff[BUFSIZE];
  188.     Widget shell;
  189.     Display *dpy = side->ui->dpy;
  190.  
  191.     DGprintf("Creating map, mag power %d\n", power);
  192.     map = (Map *) xmalloc(sizeof(Map));
  193.  
  194.     /* 1000x750 pixels is "ideal" - big, but not really big. */
  195.     map->totalw = 1000;  map->totalh = 750;
  196.     if (!empty_string(geospec)) {
  197.     /* This map has been given a preferred size - work to it. */
  198.     flags = XParseGeometry(geospec, &sx, &sy, &w, &h);
  199.     if (flags & WidthValue)
  200.       map->totalw = w;
  201.     if (flags & HeightValue)
  202.       map->totalh = h;
  203.     /* Note that we allow the caller to specify geometries
  204.        that may be larger than the available screen, on the
  205.        theory that people specifying geometries explicitly
  206.        know what they're doing. */
  207.     } else {
  208.     /* Shrink to fit display if the default is too big. */
  209.     w = DisplayWidth(dpy, side->ui->screen);
  210.     h = DisplayHeight(dpy, side->ui->screen);
  211.     /* Leave a little room around the edges, but not too much;
  212.        Xconq needs all the screen space it can get. */
  213.     map->totalw = min(map->totalw, w - w / 16);
  214.     map->totalh = min(map->totalh, h - h / 16);
  215.     }
  216.     /* Compute the vertical spacing for the side list entries. */
  217.     map->sidespacing = fh + 12;
  218.     /* Add room to display per-side clocks if necessary. */
  219.     if (g_rt_per_side() > 0) {
  220.     map->sidespacing += fh;
  221.     }
  222.     /* Add room to display scorekeepers if necessary. */
  223.     if (keeping_score()) {
  224.     map->sidespacing += ((numscorekeepers + 1) / 2) * fh;
  225.     }
  226.     subdivide_map(side, map);
  227.  
  228.     if (power < 0) {
  229.     /* We must want a world map; compute a power that will
  230.        be a good fit. */
  231.     for (power = NUMPOWERS - 2; power > 0; --power) {
  232.         if (hws[power] * area.width < map->pxw
  233.         && hcs[power] * area.height < map->pxh
  234.         && hws[power+1] * area.width >= map->pxw
  235.         && hcs[power+1] * area.height >= map->pxh)
  236.           break;
  237.     }
  238.     }
  239.     map->vp = new_vp();
  240.     set_map_power(side, map, power);
  241.  
  242.     set_view_size(map->vp, map->pxw, map->pxh);
  243.     pick_a_focus(side, &x, &y);
  244.     set_view_focus(map->vp, x, y);
  245.     /* Set default values for the display controls. */
  246.     map->drawterrain = TRUE;
  247.     map->drawgrid = TRUE;  /* should get from a resource */
  248.     map->drawcellpats = side->ui->monochrome;
  249.     map->drawunits = TRUE;
  250.     map->drawnames = FALSE /* (map->hh >= 8) */;
  251.     map->drawpeople = FALSE;
  252.     map->drawelevations = FALSE;
  253.     map->drawfeatureboundaries = FALSE;
  254.     map->drawfeaturenames = FALSE;
  255.     /* cache the highest feature number; don't forget to update this 
  256.        if new featured are added (presently not implemented)  */
  257.     side->ui->numfeatures = num_features();
  258.     place_legends(side);
  259.     for_all_material_types(m)
  260.       map->drawresources[m] = FALSE;
  261.     map->drawtemp = FALSE;
  262.     map->drawweather = FALSE;
  263.     map->seeall = all_see_all;
  264.     /* Start the map off in generic move mode normally. */
  265.     map->curtool = movetool;
  266.     map->inpch = '\0';
  267.     map->prefixarg = -1;
  268.     map->inptype = NONUTYPE;
  269.     /* Make the unit type string be empty. */
  270.     map->ustr[0] = '\0';
  271.     map->tstr[0] = '\0';
  272.     map->savedcurx = map->savedcury = -1;
  273.     /* Newest map goes on the front of the list. */
  274.     map->next = side->ui->maps;
  275.     side->ui->maps = map;
  276.  
  277.     if (map->next == NULL) {
  278.     shell = side->ui->shell;
  279.     } else {
  280.     shell = XtVaCreatePopupShell("Map", topLevelShellWidgetClass,
  281.                      side->ui->shell,                   
  282.                      XtNwidth, map->totalw,
  283.                      XtNheight, map->totalh,
  284.                      NULL);
  285. /*    XSetWMProtocols(dpy, XtWindow(shell), &side->ui->kill_atom, 1);  */
  286.     }
  287.  
  288.     /* Build up the complex of widgets that constitute the map window. */
  289.     map->mainwidget =
  290.       XtVaCreateManagedWidget("map", panedWidgetClass, shell,
  291.                   XtNwidth, map->totalw,
  292.                   XtNheight, map->totalh,
  293.                   XtNorientation, XtorientHorizontal,
  294.                   NULL);
  295.     
  296.     map->leftpane =
  297.       XtVaCreateManagedWidget("leftPane", panedWidgetClass, map->mainwidget,
  298.                   XtNwidth, map->leftw,
  299.                   XtNmin, 50,
  300.                   NULL);
  301.  
  302.     map->infoform =
  303.       XtVaCreateManagedWidget("infoForm", panedWidgetClass, map->leftpane,
  304.                   XtNmin, 150,
  305.                   NULL);
  306.  
  307.     map->history =
  308.       XtVaCreateManagedWidget("history", asciiTextWidgetClass, map->infoform,
  309.                   XtNscrollHorizontal, XawtextScrollWhenNeeded,
  310.                   XtNscrollVertical, XawtextScrollAlways,
  311.                   XtNshowGrip, False,
  312.                   NULL);
  313.  
  314.     /* It would be helpful sometimes to have a two-line input prompt window,
  315.        but it chews valuable space.  Perhaps have two lines only if map
  316.        is "large". */
  317.     map->promptlabel =
  318.       XtVaCreateManagedWidget("prompt", labelWidgetClass, map->infoform,
  319.                   XtNlabel, " ",
  320.                   XtNshowGrip, False,
  321.                   XtNskipAdjust, True,
  322.                   NULL);
  323.  
  324.     map->gamedate =
  325.       XtVaCreateManagedWidget("gameDate", labelWidgetClass, map->infoform,
  326.                   XtNlabel, " ",
  327.                   XtNborderWidth, 0,
  328.                   XtNshowGrip, False,
  329.                   XtNskipAdjust, True,
  330.                   NULL);
  331.    /* (should split gamedate and put clock on same line...) */
  332.    if (g_rt_per_turn() > 0 || g_rt_for_game() > 0)
  333.      map->gameclock =
  334.       XtVaCreateManagedWidget("gameClock", labelWidgetClass, map->infoform,
  335.                   XtNlabel, " ",
  336.                   XtNborderWidth, 0,
  337.                   XtNshowGrip, False,
  338.                   XtNskipAdjust, True,
  339.                   NULL);
  340. #if 0 /* this needs a home, but don't let it chew up screen space all the time */
  341.     map->msgarea =
  342.       XtVaCreateManagedWidget("message", labelWidgetClass, map->infoform,
  343.                   /* Make it a two line label. */
  344.                   XtNlabel, "one\ntwo",
  345.                   XtNright, XtChainRight,
  346.                   XtNleft, XtChainLeft,
  347.                   XtNtop, XtChainBottom,
  348.                   XtNbottom, XtChainBottom,
  349.                   XtNfromVert, map->gameclock,
  350.                   NULL);
  351. #endif
  352.  
  353.     map->leftform =
  354.       XtVaCreateManagedWidget("leftForm", panedWidgetClass, map->leftpane,
  355.                   XtNmin, 50,
  356.                   XtNorientation, XtorientHorizontal,
  357.                   NULL);
  358.  
  359.     map->controlform =
  360.       XtVaCreateManagedWidget("controlForm", formWidgetClass, map->leftform,
  361.                   XtNmax, 150,
  362.                   XtNmin, 50,
  363.                   NULL);
  364.     XawFormDoLayout(map->controlform, False);
  365.     create_map_controls(side, map);
  366.     XawFormDoLayout(map->controlform, True);
  367.  
  368.     map->mapform =
  369.       XtVaCreateManagedWidget("mapForm", panedWidgetClass, map->leftform,
  370.                   XtNmin, 50,
  371.                   XtNorientation, XtorientVertical,
  372.                   NULL);
  373.  
  374.     map->info =
  375.       XtVaCreateManagedWidget("info", widgetClass, map->mapform,
  376.                   XtNwidth, map->pxw,
  377.                   XtNheight, map->infoh,
  378.                   /* Insist that at least 2 lines be visible. */
  379.                   XtNmin, 2 * fh,
  380.                   /* ...might be useful to have large info window,
  381.                      so don't limit its max size. */
  382.                   NULL);
  383.     XtAddEventHandler(map->info,
  384.                   KeyPressMask|ButtonPressMask|ButtonReleaseMask
  385.               |ExposureMask|StructureNotifyMask,
  386.                   False, handle_map_info_events, NULL);
  387.  
  388.     map->porthole =
  389.       XtVaCreateManagedWidget("porthole", portholeWidgetClass, map->mapform,
  390.                   XtNwidth, map->pxw,
  391.                   XtNheight, map->pxh,
  392.                   /* Don't let the map disappear completely. */
  393.                   XtNmin, 50,
  394.                   NULL);
  395.     XtAddCallback(map->porthole, XtNreportCallback,
  396.           porthole_callback, NULL);
  397.     map->portlabel =
  398.       XtVaCreateManagedWidget("portholeLabel", labelWidgetClass, map->porthole,
  399.                   XtNinternalHeight, 0,
  400.                   XtNinternalWidth, 0,
  401.                   XtNwidth, map->pxw,
  402.                   XtNheight, map->pxh,
  403.                   NULL);
  404.  
  405.     /* The right side of the window is dedicated to lists and to the
  406.        map panner. */
  407.  
  408.     map->rightpane =
  409.       XtVaCreateManagedWidget("rightPane", panedWidgetClass, map->mainwidget,
  410.                   /* Let this side be small but not too small. */
  411.                   XtNmin, 50,
  412.                   NULL);
  413.  
  414.     map->sides =
  415.       XtVaCreateManagedWidget("sides", widgetClass, map->rightpane,
  416.                   XtNwidth, map->list1w,
  417.                   XtNheight, map->list1h,
  418.                   /* At least one side should be visible always. */
  419.                   XtNmin, map->sidespacing,
  420.                   /* No reason to grow to more than numsides. */
  421.                   XtNmax, numsides * map->sidespacing,
  422.                   NULL);
  423.     XtAddEventHandler(map->sides,
  424.                   KeyPressMask|ButtonPressMask|ButtonReleaseMask
  425.               |ExposureMask|StructureNotifyMask,
  426.                   False, handle_map_sides_events, NULL);
  427.  
  428.     map->listview =
  429.       XtVaCreateManagedWidget("listView", viewportWidgetClass, map->rightpane,
  430.                   XtNallowVert, True,
  431.                   NULL);
  432.     map->listform =
  433.       XtVaCreateManagedWidget("listForm", formWidgetClass, map->listview,
  434.                   NULL);
  435.  
  436.     map->list_buttons = (Widget *) xmalloc(sizeof (Widget) * (numutypes + 1));
  437.     for (i = 0; i <= numutypes; i++) {
  438.     pic = XCreatePixmap(dpy, side->ui->rootwin,
  439.                 min_w_for_unit_image, min_h_for_unit_image,
  440.                 DefaultDepth(dpy, side->ui->screen));
  441.     XSetForeground(dpy, side->ui->gc, side->ui->bgcolor);
  442.     XFillRectangle(dpy, pic, side->ui->gc,
  443.                0, 0, min_w_for_unit_image, min_h_for_unit_image);
  444.     XSetForeground(dpy, side->ui->gc, side->ui->fgcolor);
  445.     if (i == 0) {
  446.         map->list_buttons[i] =
  447.           XtVaCreateManagedWidget("utype_", commandWidgetClass, map->listform,
  448.                       XtNborderWidth, 0,
  449.                       XtNheight, min_h_for_unit_image + 4,
  450.                       XtNjustify, XtJustifyLeft,
  451.                       XtNlabel, "  Num(Bld)  Gain  Loss",
  452.                       XtNleftBitmap, pic,
  453.                       XtNresize, False,
  454.                       NULL);
  455. #if 0
  456.         XtVaGetValues(map->list_buttons[i],
  457.               XtNtranslations, &saved_translations,
  458.               NULL);
  459. #endif
  460.         /* We never do anything with this "button". */
  461.         XtUninstallTranslations(map->list_buttons[i]);
  462.     } else {
  463.         /* Draw the unit image into the pixmap.
  464.            NOTE: The command widget expects the leftBitmap to be in
  465.            "bitmap" format - a 1 bit gets drawn in the foreground
  466.            color and a 0 bit in the background color.
  467.            That's why we pass in 1 & 0 to draw_unit_image() here for
  468.            fgcolor & bgcolor. */
  469.         draw_unit_image(side, pic, 0, 0,
  470.                 min_w_for_unit_image, min_h_for_unit_image,
  471.                 i - 1, -1, 1, 0);
  472.  
  473.         /* Name the widget. */
  474.         build_name(wbuff, "utype_", u_type_name(i - 1));
  475.         map->list_buttons[i] =
  476.           XtVaCreateManagedWidget(wbuff, commandWidgetClass, map->listform,
  477.                       /* 0 looks better, but use 1 for debugging. */
  478.                       XtNborderWidth, 1,
  479.                       XtNheight, min_h_for_unit_image + 4,
  480.                       XtNhighlightThickness, 0,
  481.                       XtNjustify, XtJustifyLeft,
  482.                       XtNlabel, "                      ",
  483.                       XtNleftBitmap, pic,
  484.                       XtNresize, False,
  485.                       /* Tie to next button up. */
  486.                       XtNfromVert, map->list_buttons[i - 1],
  487.                       NULL);
  488.         XtAddCallback(map->list_buttons[i], XtNcallback,
  489.               unit_type_list_callback, (XtPointer) i);
  490.         update_unit_type_list(side, map, i - 1);
  491.     }
  492.     }
  493.     /* (should fix this calculation) */
  494.     sx = (map->panw * 100) / (map->vp->totsw - hexagon_adjust(map->vp));
  495.     sy = (map->panh * 100) / map->vp->totsh;
  496.     scale = min(sx, sy);
  497.  
  498.     map->pannerbox =
  499.       XtVaCreateManagedWidget("pannerBox", boxWidgetClass, map->rightpane,
  500.                   XtNmin, map->panh + 8,
  501.                   XtNmax, map->panh + 8,
  502.                   XtNshowGrip, False,
  503.                   XtNskipAdjust, True,
  504.                   NULL);
  505.     map->panner =
  506.       XtVaCreateManagedWidget("panner", pannerWidgetClass, map->pannerbox,
  507.                   XtNwidth, map->panw,
  508.                   XtNheight, map->panh,
  509.                   XtNcanvasWidth, map->vp->totsw - hexagon_adjust(map->vp),
  510.                   XtNcanvasHeight, map->vp->totsh,
  511.                   XtNdefaultScale, scale,
  512.                   /* Don't let the panner's size change,
  513.                  should recalculate the scale instead. */
  514.                   XtNresize, False,
  515.                   XtNrubberBand, True,
  516.                   XtNsliderWidth, map->pxw,
  517.                   XtNsliderHeight, map->pxh,
  518.                   NULL);
  519.     XtAddCallback(map->panner, XtNreportCallback,
  520.           panner_callback, NULL);
  521.  
  522.     XtAddEventHandler(map->panner, StructureNotifyMask, False,
  523.               panner_resize_handler, NULL);
  524.  
  525.     XtRealizeWidget(shell);
  526.  
  527.     x_center_on_focus(side, map);
  528.     clear_prompt(side, map);
  529.  
  530.     /* Make the display appear. */
  531.     XtPopup(shell, XtGrabNone);
  532.  
  533.     /* Set the cursor to match the current tool. */
  534.     set_tool_cursor(side, map);
  535.  
  536.     /* Cache some windows into their own slots, for convenience later. */
  537.     map->infowin = XtWindow(map->info);
  538.     map->viewwin = XtWindow(map->portlabel);
  539.     map->sideswin = XtWindow(map->sides);
  540.  
  541.     /* initialize panner pixmap */
  542.     map->panner_pix = None;
  543.  
  544.     draw_map(side, map);
  545.  
  546.     flush_output(side);
  547.  
  548. #if 0  /* there seems not to be any way to flush out widget state tweaks
  549.       if this is done - feel free to prove me wrong! */
  550.     for (i = 0; i <= numutypes; i++) {
  551.     /* Don't do anything if mouse passes over. */
  552.     XtUninstallTranslations(map->list_buttons[i]);
  553.     }
  554. #endif
  555.  
  556.     return map;
  557. }
  558.  
  559. static void
  560. create_map_controls(side, map)
  561. Side *side;
  562. Map *map;
  563. {
  564.     int wid = 80;
  565.  
  566.     map->controls = (Widget *) xmalloc(numcontrols * sizeof(Widget));
  567.  
  568.     map->controls[LOOK] =
  569.       XtVaCreateManagedWidget("surveyMode", toggleWidgetClass, map->controlform,
  570.                   XtNlabel, "Survey",
  571. #if 1 /* X11R5 or later */
  572.                   XtNleftBitmap, side->ui->controlpics[LOOK],
  573. #endif
  574.                   XtNresize, False,
  575.                   XtNwidth, wid,
  576.                   NULL);
  577.     XtAddCallback(map->controls[LOOK], XtNcallback,
  578.           mode_callback, (XtPointer) LOOK);
  579.     map->controls[MOVE] =
  580.       XtVaCreateManagedWidget("moveMode", toggleWidgetClass, map->controlform,
  581.                   XtNlabel, " Move ",
  582. #if 1 /* X11R5 or later */
  583.                   XtNleftBitmap, side->ui->controlpics[MOVE],
  584. #endif
  585.                   XtNresize, False,
  586.                   XtNwidth, wid,
  587.                   XtNfromVert, map->controls[LOOK],
  588.                   NULL);
  589.     XtAddCallback(map->controls[MOVE], XtNcallback,
  590.           mode_callback, (XtPointer) MOVE);
  591.  
  592.     map->controls[UNIT_MOVE] =
  593.       XtVaCreateManagedWidget("unitMove", commandWidgetClass, map->controlform,
  594.                   XtNlabel, "MoveTo",
  595. #if 1 /* X11R5 or later */
  596.                   XtNleftBitmap, side->ui->controlpics[UNIT_MOVE],
  597. #endif
  598.                   XtNresize, False,
  599.                   XtNwidth, wid,
  600.                   XtNfromVert, map->controls[MOVE],
  601.                   XtNvertDistance, 10,
  602.                   NULL);
  603.     XtAddCallback(map->controls[UNIT_MOVE], XtNcallback,
  604.           command_callback, (XtPointer) UNIT_MOVE);
  605.     map->controls[UNIT_SHOOT] =
  606.       XtVaCreateManagedWidget("unitFire", commandWidgetClass, map->controlform,
  607.                   XtNlabel, "Fire",
  608. #if 1 /* X11R5 or later */
  609.                   XtNleftBitmap, side->ui->controlpics[UNIT_SHOOT],
  610. #endif
  611.                   XtNresize, False,
  612.                   XtNwidth, wid,
  613.                   XtNfromVert, map->controls[UNIT_MOVE],
  614.                   NULL);
  615.     XtAddCallback(map->controls[UNIT_SHOOT], XtNcallback,
  616.           command_callback, (XtPointer) UNIT_SHOOT);
  617.     map->controls[UNIT_BUILD] =
  618.       XtVaCreateManagedWidget("unitBuild", commandWidgetClass, map->controlform,
  619.                   XtNlabel, "Build",
  620. #if 1 /* X11R5 or later */
  621.                   XtNleftBitmap, side->ui->controlpics[UNIT_BUILD],
  622. #endif
  623.                   XtNresize, False,
  624.                   XtNwidth, wid,
  625.                   XtNfromVert, map->controls[UNIT_SHOOT],
  626.                   NULL);
  627.     XtAddCallback(map->controls[UNIT_BUILD], XtNcallback,
  628.           command_callback, (XtPointer) UNIT_BUILD);
  629.  
  630.     map->controls[SHOW_TERRAIN] =
  631.       XtVaCreateManagedWidget("terrain", toggleWidgetClass, map->controlform,
  632.                   XtNlabel, "Terrain",
  633.                   XtNresize, False,
  634.                   XtNwidth, wid,
  635.                   XtNfromVert, map->controls[UNIT_BUILD],
  636.                   XtNvertDistance, 10,
  637.                   NULL);
  638.     XtAddCallback(map->controls[SHOW_TERRAIN], XtNcallback,
  639.           view_flag_callback, (XtPointer) SHOW_TERRAIN);
  640.     map->controls[SHOW_GRID] =
  641.       XtVaCreateManagedWidget("grid", toggleWidgetClass, map->controlform,
  642.                   XtNlabel, "Grid",
  643.                   XtNresize, False,
  644.                   XtNwidth, wid,
  645.                   XtNfromVert, map->controls[SHOW_TERRAIN],
  646.                   NULL);
  647.     XtAddCallback(map->controls[SHOW_GRID], XtNcallback,
  648.           view_flag_callback, (XtPointer) SHOW_GRID);
  649.     map->controls[SHOW_UNITS] =
  650.       XtVaCreateManagedWidget("units", toggleWidgetClass, map->controlform,
  651.                   XtNlabel, "Units",
  652.                   XtNresize, False,
  653.                   XtNwidth, wid,
  654.                   XtNfromVert, map->controls[SHOW_GRID],
  655.                   NULL);
  656.     XtAddCallback(map->controls[SHOW_UNITS], XtNcallback,
  657.           view_flag_callback, (XtPointer) SHOW_UNITS);
  658.     map->controls[SHOW_NAMES] =
  659.       XtVaCreateManagedWidget("names", toggleWidgetClass, map->controlform,
  660.                   XtNlabel, "Names",
  661.                   XtNresize, False,
  662.                   XtNwidth, wid,
  663.                   XtNfromVert, map->controls[SHOW_UNITS],
  664.                   NULL);
  665.     XtAddCallback(map->controls[SHOW_NAMES], XtNcallback,
  666.           view_flag_callback, (XtPointer) SHOW_NAMES);
  667.     map->controls[SHOW_PEOPLE] =
  668.       XtVaCreateManagedWidget("people", toggleWidgetClass, map->controlform,
  669.                   XtNlabel, "People",
  670.                   XtNresize, False,
  671.                   XtNwidth, wid,
  672.                   XtNfromVert, map->controls[SHOW_NAMES],
  673.                   NULL);
  674.     XtAddCallback(map->controls[SHOW_PEOPLE], XtNcallback,
  675.           view_flag_callback, (XtPointer) SHOW_PEOPLE);
  676.     map->controls[ZOOM_OUT] =
  677.       XtVaCreateManagedWidget("zoomOut", commandWidgetClass, map->controlform,
  678.                   XtNbitmap, side->ui->controlpics[ZOOM_OUT],
  679.                   XtNresize, False,
  680.                   XtNwidth, wid / 2 - 3,
  681.                   XtNfromVert, map->controls[SHOW_PEOPLE],
  682.                   XtNvertDistance, 10,
  683.                   NULL);
  684.     XtAddCallback(map->controls[ZOOM_OUT], XtNcallback,
  685.           zoom_callback, (XtPointer) ZOOM_OUT);
  686.     map->controls[ZOOM_IN] =
  687.       XtVaCreateManagedWidget("zoomIn", commandWidgetClass, map->controlform,
  688.                   XtNbitmap, side->ui->controlpics[ZOOM_IN],
  689.                   XtNresize, False,
  690.                   XtNwidth, wid / 2 - 3,
  691.                   XtNfromVert, map->controls[SHOW_PEOPLE],
  692.                   XtNfromHoriz, map->controls[ZOOM_OUT],
  693.                   XtNvertDistance, 10,
  694.                   NULL);
  695.     XtAddCallback(map->controls[ZOOM_IN], XtNcallback,
  696.           zoom_callback, (XtPointer) ZOOM_IN);
  697.     map->controls[SHOW_ALL] =
  698.       XtVaCreateManagedWidget("all", toggleWidgetClass, map->controlform,
  699.                   XtNlabel, "All",
  700.                   XtNresize, False,
  701.                   XtNsensitive, !all_see_all,
  702.                   XtNwidth, wid,
  703.                   XtNfromVert, map->controls[ZOOM_OUT],
  704.                   XtNvertDistance, 10,
  705.                   NULL);
  706.     XtAddCallback(map->controls[SHOW_ALL], XtNcallback,
  707.           view_flag_callback, (XtPointer) SHOW_ALL);
  708.     map->controls[SHOW_MORE] =
  709.       XtVaCreateManagedWidget("more", toggleWidgetClass, map->controlform,
  710.                   XtNlabel, "More...",
  711.                   XtNresize, False,
  712.                   XtNwidth, wid,
  713.                   XtNfromVert, map->controls[SHOW_ALL],
  714.                   NULL);
  715.     XtAddCallback(map->controls[SHOW_MORE], XtNcallback,
  716.           view_flag_callback, (XtPointer) SHOW_MORE);
  717.  
  718.     map->controls[POPUP_HELP] =
  719.       XtVaCreateManagedWidget("help", toggleWidgetClass, map->controlform,
  720.                   XtNlabel, "Help",
  721.                   XtNresize, False,
  722.                   XtNwidth, wid,
  723.                   XtNfromVert, map->controls[SHOW_MORE],
  724.                   NULL);
  725.     XtAddCallback(map->controls[POPUP_HELP], XtNcallback,
  726.           map_help_callback, NULL);
  727.  
  728.  
  729.     update_controls(side, map);
  730. }
  731.  
  732. void
  733. set_map_power(side, map, power)
  734. Side *side;
  735. Map *map;
  736. int power;
  737. {
  738.     set_view_power(map->vp, power);
  739.     if (map->panner) {
  740.     XtVaSetValues(map->panner,
  741.               XtNcanvasWidth, map->vp->totsw - hexagon_adjust(map->vp),
  742.               XtNcanvasHeight, map->vp->totsh,
  743.               /* what about slider size? */
  744.               NULL);
  745.     }
  746. }
  747.  
  748. void
  749. x_center_on_focus(side, map)
  750. Side *side;
  751. Map *map;
  752. {
  753.     center_on_focus(map->vp);
  754.     if (map->panner) {
  755.     XtVaSetValues(map->panner,
  756.               XtNsliderX, map->vp->sx - hexagon_adjust(map->vp),
  757.               XtNsliderY, map->vp->sy,
  758.               NULL);
  759.     }
  760. }
  761.  
  762. static void
  763. MA_setcoord(w, event, params, num_params)
  764. Widget w;
  765. XEvent *event;
  766. String *params;
  767. Cardinal *num_params;
  768. {
  769.     int cellx, celly;
  770.     Side *side;
  771.     Map *map;
  772.     XButtonEvent *btn = &event->xbutton;
  773.  
  774.     if (!find_side_and_map_via_porthole(w, &side, &map))
  775.       return;
  776.  
  777.     side->ui->mapdown = map;
  778.     side->ui->sxdown = btn->x;  side->ui->sydown = btn->y;
  779.     side->ui->cellxy_ok = x_nearest_cell(side, map, btn->x, btn->y, &cellx, &celly);
  780.     if (side->ui->cellxy_ok) {
  781.     side->ui->cellx = cellx;  side->ui->celly = celly;
  782.     }
  783. }
  784.  
  785. static void
  786. MA_motionsetcoord(w, event, params, num_params)
  787. Widget w;
  788. XEvent *event;
  789. String *params;
  790. Cardinal *num_params;
  791. {
  792.     int cellx, celly;
  793.     Side *side;
  794.     Map *map;
  795.     XMotionEvent *mtn = &event->xmotion;
  796.  
  797.     if (!find_side_and_map_via_porthole(w, &side, &map))
  798.       return;
  799.  
  800.     side->ui->mapdown = map;
  801.     side->ui->sxdown = mtn->x;  side->ui->sydown = mtn->y;
  802.     side->ui->cellxy_ok = x_nearest_cell(side, map, mtn->x, mtn->y, &cellx, &celly);
  803.     if (side->ui->cellxy_ok) {
  804.     side->ui->cellx = cellx;  side->ui->celly = celly;
  805.     }
  806. }
  807.  
  808. static void
  809. MA_movelook(w, event, params, num_params)
  810. Widget w;
  811. XEvent *event;
  812. String *params;
  813. Cardinal *num_params;
  814. {
  815.     int cellx, celly;
  816.     Unit *unit;
  817.     Side *side;
  818.     Map *map;
  819.  
  820.     if (!find_side_and_map_via_porthole(w, &side, &map))
  821.       return;
  822.  
  823.     if (side->ui->cellxy_ok) {
  824.     cellx = side->ui->cellx;  celly = side->ui->celly;
  825.     if (inside_area(cellx, celly)) {
  826.         x_nearest_unit(side, map, side->ui->sxdown, side->ui->sydown, &unit);
  827.         if (unit != NULL &&
  828.         (side_controls_unit(side, unit) || map->seeall)) {
  829.         set_current_unit(side, map, unit);
  830.         } else {
  831.         set_current_xy(side, map, cellx, celly);
  832.         }
  833.     } else {
  834.         beep(side);
  835.     }
  836.     }
  837. }
  838.  
  839. static void
  840. MA_movelook_ul(w, event, params, num_params)
  841. Widget w;
  842. XEvent *event;
  843. String *params;
  844. Cardinal *num_params;
  845. {
  846.     movelook_one_cell(w, dirx[NORTHWEST], diry[NORTHWEST]);
  847. }
  848.  
  849. static void
  850. MA_movelook_l(w, event, params, num_params)
  851. Widget w;
  852. XEvent *event;
  853. String *params;
  854. Cardinal *num_params;
  855. {
  856.     movelook_one_cell(w, dirx[WEST], diry[WEST]);
  857. }
  858.  
  859. static void
  860. MA_movelook_dl(w, event, params, num_params)
  861. Widget w;
  862. XEvent *event;
  863. String *params;
  864. Cardinal *num_params;
  865. {
  866.     movelook_one_cell(w, dirx[SOUTHWEST], diry[SOUTHWEST]);
  867. }
  868.  
  869. static void
  870. MA_movelook_ur(w, event, params, num_params)
  871. Widget w;
  872. XEvent *event;
  873. String *params;
  874. Cardinal *num_params;
  875. {
  876.     movelook_one_cell(w, dirx[NORTHEAST], diry[NORTHEAST]);
  877. }
  878.  
  879. static void
  880. MA_movelook_r(w, event, params, num_params)
  881. Widget w;
  882. XEvent *event;
  883. String *params;
  884. Cardinal *num_params;
  885. {
  886.     movelook_one_cell(w, dirx[EAST], diry[EAST]);
  887. }
  888.  
  889. static void
  890. MA_movelook_dr(w, event, params, num_params)
  891. Widget w;
  892. XEvent *event;
  893. String *params;
  894. Cardinal *num_params;
  895. {
  896.     movelook_one_cell(w, dirx[SOUTHEAST], diry[SOUTHEAST]);
  897. }
  898.  
  899. static void
  900. movelook_one_cell(w, xdelta, ydelta)
  901. Widget w;
  902. int xdelta, ydelta;
  903. {
  904.     Side *side;
  905.     Map *map;
  906.     Unit *unit;
  907.     int nx, ny;
  908.  
  909.     if (!find_side_and_map_via_porthole(w, &side, &map))
  910.       return;
  911.  
  912.     nx = map->curx + xdelta;  ny = map->cury + ydelta;
  913.     if (inside_area(nx, ny)) {
  914.     unit = unit_at(nx, ny);
  915.     if (unit != NULL &&
  916.         (side_controls_unit(side, unit) || map->seeall)) {
  917.         set_current_unit(side, map, unit);
  918.     } else {
  919.         set_current_xy(side, map, nx, ny);
  920.     }
  921.     } else {
  922.     beep(side);
  923.     }
  924. }
  925.  
  926. static void
  927. MA_center(w, event, params, num_params)
  928. Widget w;
  929. XEvent *event;
  930. String *params;
  931. Cardinal *num_params;
  932. {
  933.     Side *side;
  934.     Map *map;
  935.  
  936.     if (!find_side_and_map_via_porthole(w, &side, &map))
  937.       return;
  938.  
  939.     recenter(side, map, map->curx, map->cury);
  940. }
  941.  
  942. static void
  943. MA_moveunit(w, event, params, num_params)
  944. Widget w;
  945. XEvent *event;
  946. String *params;
  947. Cardinal *num_params;
  948. {
  949.     Side *side;
  950.     Map *map;
  951.  
  952.     if (!find_side_and_map_via_porthole(w, &side, &map))
  953.       return;
  954.  
  955.     if (side->ui->cellxy_ok) {
  956.     if (map->curunit && side_controls_unit(side, map->curunit)) {
  957.         move_the_selected_unit(side, map, map->curunit,
  958.                    side->ui->sxdown, side->ui->sydown);
  959.     } else {
  960.         beep(side);
  961.     }
  962.     }
  963. }
  964.  
  965. static void
  966. MA_distance(w, event, params, num_params)
  967. Widget w;
  968. XEvent *event;
  969. String *params;
  970. Cardinal *num_params;
  971. {
  972.     Side *side;
  973.     Map *map;
  974.     XButtonEvent *btn = &event->xbutton;
  975.     int upcellx, upcelly;
  976.     int dist;
  977.  
  978.     if (!find_side_and_map_via_porthole(w, &side, &map))
  979.       return;
  980.     if (side->ui->cellxy_ok) {
  981.     if (x_nearest_cell(side, map, btn->x, btn->y, &upcellx, &upcelly)) {
  982.         dist = distance(side->ui->cellx, side->ui->celly, upcellx, upcelly);
  983.         notify(side, "The distance is %d cells.", dist);
  984.     } else {
  985.         beep(side);
  986.     }
  987.     }
  988. }
  989.  
  990. static void
  991. MA_keypress(w, event, params, num_params)
  992. Widget w;
  993. XEvent *event;
  994. String *params;
  995. Cardinal *num_params;
  996. {
  997.     Side *side;
  998.     Map *map;
  999.     XKeyEvent *key = &event->xkey;
  1000.  
  1001.     if (!find_side_and_map_via_porthole(w, &side, &map))
  1002.       return;
  1003.  
  1004.     side->ui->mapdown = map;
  1005.     side->ui->sxdown = key->x;  side->ui->sydown = key->y;
  1006.     handle_key_event(side, map, event);
  1007. }
  1008.  
  1009. static void
  1010. MA_mapexpose(w, event, params, num_params)
  1011. Widget w;
  1012. XEvent *event;
  1013. String *params;
  1014. Cardinal *num_params;
  1015. {
  1016.     Side *side;
  1017.     Map *map;
  1018.     XEvent qevent;
  1019.  
  1020.     if (!find_side_and_map_via_porthole(w, &side, &map))
  1021.       return;
  1022.  
  1023.     /* Do nothing if more expose events are following. */
  1024.     if (event->type == Expose && event->xexpose.count > 0)
  1025.       return;
  1026.  
  1027.     /* Clear the queue of other Expose event directed to viewwin. */
  1028.     while (XCheckTypedWindowEvent(side->ui->dpy, map->viewwin,
  1029.                   Expose, &qevent))
  1030.       ;
  1031.  
  1032.     draw_map(side, map);
  1033. }
  1034.  
  1035. static void
  1036. MA_toolaction(w, event, params, num_params)
  1037. Widget w;
  1038. XEvent *event;
  1039. String *params;
  1040. Cardinal *num_params;
  1041. {
  1042.     Side *side;
  1043.     Map *map;
  1044.     XButtonEvent *btn = &event->xbutton;
  1045.     
  1046.     if (!find_side_and_map_via_porthole(w, &side, &map))
  1047.       return;
  1048.  
  1049.     handle_map_click(side, map, btn->x, btn->y);
  1050. }
  1051.  
  1052. /* Set the current designer paint/add/etc type from what's under the cursor. */
  1053.  
  1054. static void
  1055. MA_toolselection(w, event, params, num_params)
  1056. Widget w;
  1057. XEvent *event;
  1058. String *params;
  1059. Cardinal *num_params;
  1060. {
  1061.     Side *side;
  1062.     Map *map;
  1063.     XButtonEvent *btn = &event->xbutton;
  1064.  
  1065.     if (!find_side_and_map_via_porthole(w, &side, &map))
  1066.       return;
  1067.  
  1068. #ifdef DESIGNERS
  1069.     set_designer_cur_from_map(side, map, btn->x, btn->y);
  1070. #else
  1071.     beep(side);
  1072. #endif /* DESIGNERS */
  1073. }
  1074.  
  1075. int
  1076. find_side_and_map_via_control(w, sidep, mapp)
  1077. Widget w;
  1078. Side **sidep;
  1079. Map **mapp;
  1080. {
  1081.     Side *side;
  1082.     Map *map;
  1083.  
  1084.     for_all_sides(side) {
  1085.     if (active_display(side)) {
  1086.         if (XtDisplay(w) == side->ui->dpy) {
  1087.         for_all_maps(side, map) {
  1088.             if (map->controlform == XtParent(w)) {
  1089.             *sidep = side;
  1090.             *mapp = map;
  1091.             return TRUE;
  1092.             }
  1093.         }
  1094.         }
  1095.     }
  1096.     }
  1097.     return FALSE;
  1098. }
  1099.  
  1100. int
  1101. find_side_and_map_via_a_toplevel(w, sidep, mapp)
  1102. Widget w;
  1103. Side **sidep;
  1104. Map **mapp;
  1105. {
  1106.     Side *side;
  1107.     Map *map;
  1108.  
  1109.     for_all_sides(side) {
  1110.     if (active_display(side)) {
  1111.         if (XtDisplay(w) == side->ui->dpy) {
  1112.         for_all_maps(side, map) {
  1113.             if (side->ui->shell == w) {
  1114.             *sidep = side;
  1115.             *mapp = map;
  1116.             return TRUE;
  1117.             }
  1118.             if (side->ui->help_shell == w) {
  1119.             *sidep = side;
  1120.             *mapp = map;
  1121.             return TRUE;
  1122.             }
  1123. #ifdef DESIGNERS
  1124.             if (side->ui->design_shell == w) {
  1125.             *sidep = side;
  1126.             *mapp = map;
  1127.             return TRUE;
  1128.             }
  1129. #endif /* DESIGNERS */
  1130.         }
  1131.         }
  1132.     }
  1133.     }
  1134.     return FALSE;
  1135. }
  1136.  
  1137. int
  1138. find_side_and_map_via_listform(w, sidep, mapp)
  1139. Widget w;
  1140. Side **sidep;
  1141. Map **mapp;
  1142. {
  1143.     Side *side;
  1144.     Map *map;
  1145.  
  1146.     for_all_sides(side) {
  1147.     if (active_display(side)) {
  1148.         if (XtDisplay(w) == side->ui->dpy) {
  1149.         for_all_maps(side, map) {
  1150.             if (map->listform == XtParent(w)) {
  1151.             *sidep = side;
  1152.             *mapp = map;
  1153.             return TRUE;
  1154.             }
  1155.         }
  1156.         }
  1157.     }
  1158.     }
  1159.     return FALSE;
  1160. }
  1161.  
  1162. int
  1163. find_side_and_map_via_mapform(w, sidep, mapp)
  1164. Widget w;
  1165. Side **sidep;
  1166. Map **mapp;
  1167. {
  1168.     Side *side;
  1169.     Map *map;
  1170.  
  1171.     for_all_sides(side) {
  1172.        /* don't check side->ui->active, or we'll break porthole_callback; Massimo */ if (side && side->ui) {
  1173.         if (XtDisplay(w) == side->ui->dpy) {
  1174.         for_all_maps(side, map) {
  1175.             if (map->mapform == XtParent(w)) {
  1176.             *sidep = side;
  1177.             *mapp = map;
  1178.             return TRUE;
  1179.             }
  1180.         }
  1181.         }
  1182.     }
  1183.     }
  1184.     return FALSE;
  1185. }
  1186.  
  1187. int
  1188. find_side_and_map_via_porthole(w, sidep, mapp)
  1189. Widget w;
  1190. Side **sidep;
  1191. Map **mapp;
  1192. {
  1193.     Side *side;
  1194.     Map *map;
  1195.  
  1196.     for_all_sides(side) {
  1197.     if (active_display(side)) {
  1198.         if (XtDisplay(w) == side->ui->dpy) {
  1199.         for_all_maps(side, map) {
  1200.             if (map->porthole == XtParent(w)) {
  1201.             *sidep = side;
  1202.             *mapp = map;
  1203.             return TRUE;
  1204.             }
  1205.         }
  1206.         }
  1207.     }
  1208.     }
  1209.     return FALSE;
  1210. }
  1211.  
  1212. int
  1213. find_side_and_map_via_rightform(w, sidep, mapp)
  1214. Widget w;
  1215. Side **sidep;
  1216. Map **mapp;
  1217. {
  1218.     Side *side;
  1219.     Map *map;
  1220.  
  1221.     for_all_sides(side) {
  1222.     if (active_display(side)) {
  1223.         if (XtDisplay(w) == side->ui->dpy) {
  1224.         for_all_maps(side, map) {
  1225.             if (map->rightpane == XtParent(w)
  1226.                 || map->rightpane == XtParent(XtParent(w))) {
  1227.             *sidep = side;
  1228.             *mapp = map;
  1229.             return TRUE;
  1230.             }
  1231.         }
  1232.         }
  1233.     }
  1234.     }
  1235.     return FALSE;
  1236. }
  1237.  
  1238. /* Interpret a keyboard event, either as a single-key command or as
  1239.    part of some modal interaction. */
  1240.  
  1241. void
  1242. handle_key_event(side, map, evt)
  1243. Side *side;
  1244. Map *map;
  1245. XEvent *evt;
  1246. {
  1247.     char buf[4], ch;
  1248.     int nchar, cancelled;
  1249.     void (*fn) PARAMS ((Side *sidex, Map *mapx, int cancelledx));
  1250.  
  1251.     nchar = XLookupString(&(evt->xkey), buf, 4, NULL, NULL);
  1252.     if (nchar > 0) {
  1253.     /* Collect only the first char of a possible sequence. */
  1254.     ch = buf[0];
  1255.     DGprintf("%s typed char '%c'\n", side_desig(side), ch);
  1256.     /* so we can do ^L even while typing a string */
  1257.     if (ch == REDRAW_CHAR) {
  1258.         redraw(side);
  1259.     } else if (map != NULL) {
  1260.         /* Save the char, we'll need it. */
  1261.         map->inpch = ch;
  1262.         /* Call the modal handler if defined, giving it side
  1263.            and cancel flag. */
  1264.         if (map->modalhandler) {
  1265.         fn = map->modalhandler;
  1266.         cancelled = (ch == ESCAPE_CHAR);
  1267.         /* Remove the handler - will restore itself if needed. */
  1268.         map->modalhandler = NULL;
  1269.         (*fn)(side, map, cancelled);
  1270.         if (cancelled) {
  1271.             /* Clean up any leftover interaction. */
  1272.             clear_prompt(side, map);
  1273.             notify(side, "Cancelled.");
  1274.         }
  1275.         } else {
  1276.         interp_key_command(side, map);
  1277.         }
  1278.     } else {
  1279.         beep(side);
  1280.     }
  1281.     } else {
  1282.     /* pretty strange, ok to do nothing? */
  1283.     }
  1284. }
  1285.  
  1286. static void
  1287. interp_key_command(side, map)
  1288. Side *side;
  1289. Map *map;
  1290. {
  1291.     DGprintf("%s keyboard input: %c (%d)\n",
  1292.          side_desig(side), map->inpch, map->prefixarg);
  1293.     if (isdigit(map->inpch)) {
  1294.     if (map->prefixarg < 0) {
  1295.         map->prefixarg = 0;
  1296.     } else {
  1297.         map->prefixarg *= 10;
  1298.     }
  1299.     map->prefixarg += (map->inpch - '0');
  1300.         sprintf(map->prompt, "%d:", map->prefixarg);
  1301.     map->answer[0] = '\0';
  1302.     draw_prompt(side, map);
  1303.     return;
  1304.     } else if (map->inpch == BACKSPACE_CHAR || map->inpch == DELETE_CHAR) {
  1305.     beep(side);
  1306.     } else if (map->inpch == ESCAPE_CHAR) {
  1307.     map->prefixarg = -1;
  1308.     clear_prompt(side, map);
  1309.     } else {
  1310.     clear_prompt(side, map);
  1311.     map->frombutton = FALSE;
  1312.     execute_command(side, map);
  1313.     /* Reset the argument for next time. */
  1314.     map->prefixarg = -1;
  1315.     }
  1316. }
  1317.  
  1318. void
  1319. handle_map_sides_events (w, clientdata, evt, contdispatch)
  1320. Widget w;
  1321. XtPointer clientdata;
  1322. XEvent *evt;
  1323. Boolean *contdispatch;
  1324. {
  1325.     int s;
  1326.     Side *side;
  1327.     Map *map;
  1328.  
  1329.     if (find_side_and_map_via_rightform(w, &side, &map)) {
  1330.     switch (evt->type) {
  1331.       case KeyPress:
  1332.         handle_key_event(side, map, evt);
  1333.         break;
  1334.       case ButtonPress:
  1335.         s = evt->xbutton.y / (side->ui->fh + 12) + 1;
  1336.         if (s < 0)
  1337.           s = 0;
  1338.         if (s > numsides) 
  1339.           s = numsides;
  1340. #ifdef DESIGNERS
  1341.         if (side->designer && side->ui->design_shell) {
  1342.         side->ui->cursidenumber = s;
  1343.         update_cursidenumber(side);
  1344.         }
  1345. #endif /* DESIGNERS */
  1346.         break;
  1347.       case Expose:
  1348.         draw_map_sides(side, map);
  1349.         break;
  1350.       default:
  1351.         DGprintf("Unhandled X event type %d, ignoring\n", evt->type);
  1352.         break;
  1353.     }
  1354.     }
  1355. }
  1356.  
  1357. void
  1358. handle_map_info_events (w, clientdata, evt, contdispatch)
  1359. Widget w;
  1360. XtPointer clientdata;
  1361. XEvent *evt;
  1362. Boolean *contdispatch;
  1363. {
  1364.     Side *side;
  1365.     Map *map;
  1366.  
  1367.     if (find_side_and_map_via_mapform(w, &side, &map)) {
  1368.     switch (evt->type) {
  1369.       case KeyPress:
  1370.         handle_key_event(side, map, evt);
  1371.         break;
  1372.       case ButtonPress:
  1373.         beep(side);
  1374.         break;
  1375.       case Expose:
  1376.         draw_map_info(side, map);
  1377.         break;
  1378.       default:
  1379.         DGprintf("Unhandled X event type %d, ignoring\n", evt->type);
  1380.         break;
  1381.     }
  1382.     }
  1383. }
  1384.  
  1385. /* Prompt for a type of a unit from player, maybe only allowing some types
  1386.    to be accepted.  Also allow specification of no unit type.  We do this
  1387.    by scanning the vector, building a string of chars and a vector of
  1388.    unit types, so as to be able to map back when done. */
  1389.  
  1390. int
  1391. ask_unit_type(side, map, prompt, possibles, handler)
  1392. Side *side;
  1393. Map *map;
  1394. char *prompt;
  1395. int *possibles;
  1396. void (*handler)();
  1397. {
  1398.     int u, numtypes = 0;
  1399.  
  1400.     for_all_unit_types(u) {
  1401.     if (possibles == NULL || possibles[u]) {
  1402.         map->uvec[numtypes] = u;
  1403.         map->ustr[numtypes] = utype_name_n(u, 1)[0];
  1404.         ++numtypes;
  1405.         enable_in_unit_type_list(side, map, u, 1);
  1406.     } else {
  1407.         enable_in_unit_type_list(side, map, u, -1);
  1408.     }
  1409.     }
  1410.     map->ustr[numtypes] = '\0';
  1411.     if (numtypes > 1) {
  1412.     sprintf(map->prompt, "%s [%s]", prompt, map->ustr);
  1413.     map->answer[0] = '\0';
  1414.     draw_prompt(side, map);
  1415.     map->modalhandler = handler;
  1416.     }
  1417.     return numtypes;
  1418. }
  1419.  
  1420. /* Do something with the char or unit type that the player entered. */
  1421.  
  1422. int
  1423. grok_unit_type(side, map, typep)
  1424. Side *side;
  1425. Map *map;
  1426. int *typep;
  1427. {
  1428.     int i, u;
  1429.  
  1430.     *typep = NONUTYPE;
  1431.     if (map->inptype != NONUTYPE) {
  1432.     *typep = map->inptype;
  1433.     /* Reset so doesn't affect subsequent unit type queries. */
  1434.     map->inptype = NONUTYPE;
  1435.     } else if (map->inpch != '\0') {
  1436.     if (map->inpch == '?') {
  1437.         help_unit_type(side, map);
  1438.         return FALSE;
  1439.     }
  1440.     i = iindex(map->inpch, map->ustr);
  1441.     if (i >= 0) {
  1442.         *typep = map->uvec[i];
  1443.     } else {
  1444.         notify(side, "Must type a unit type char from the list, or <esc>");
  1445.         return FALSE;
  1446.     }
  1447.     } else {
  1448.     notify(side, "weird");
  1449.     return FALSE;
  1450.     }
  1451.     clear_prompt(side, map);
  1452.     /* Reset all the buttons in the unit type list. */
  1453.     for_all_unit_types(u) {
  1454.     enable_in_unit_type_list(side, map, u, 0);
  1455.     }
  1456.     /* Make the unit type string be empty. */
  1457.     map->ustr[0] = '\0';
  1458.     return TRUE;
  1459. }
  1460.  
  1461. static void
  1462. help_unit_type(side, map)
  1463. Side *side;
  1464. Map *map;
  1465. {
  1466.     int i;
  1467.     char helpbuf[BUFSIZE];
  1468.  
  1469.     for (i = 0; map->ustr[i] != '\0'; ++i) {
  1470.     if (i % 4 == 0) {
  1471.         if (i > 0) {
  1472.         tprintf(helpbuf, "? for this help info"); 
  1473.         notify(side, "%s", helpbuf);
  1474.         }
  1475.         helpbuf[0] = '\0';
  1476.     }
  1477.     tprintf(helpbuf, "%c %s", map->ustr[i], u_type_name(map->uvec[i]));
  1478.     if (i > 0)
  1479.       tprintf(helpbuf, ", ");
  1480.     }
  1481. }
  1482.  
  1483. void
  1484. enable_in_unit_type_list(side, map, u, flag)
  1485. Side *side;
  1486. Map *map;
  1487. int u, flag;
  1488. {
  1489.     switch (flag) {
  1490.       case -1:
  1491.     XtVaSetValues(map->list_buttons[u + 1],
  1492.               XtNsensitive, False,
  1493. #if 0
  1494.               XtNtranslations, saved_translations,
  1495. #endif
  1496.               NULL);
  1497.     break;
  1498.       case 0:
  1499.     /* Tweak the button to how it should look when disabled. */
  1500.     XtVaSetValues(map->list_buttons[u + 1],
  1501.               XtNsensitive, True,
  1502.               NULL);
  1503. #if 0
  1504.     /* (need to force button state out somehow?) */
  1505.     XtUninstallTranslations(map->list_buttons[u + 1]);
  1506. #endif
  1507.     break;
  1508.       case 1:
  1509.     XtVaSetValues(map->list_buttons[u + 1],
  1510.               XtNhighlightThickness, 2,
  1511.               XtNsensitive, True,
  1512. #if 0
  1513.               XtNtranslations, saved_translations,
  1514. #endif
  1515.               NULL);
  1516.     break;
  1517.     }
  1518. }
  1519.  
  1520. int
  1521. ask_terrain_type(side, map, prompt, possibles, handler)
  1522. Side *side;
  1523. Map *map;
  1524. char *prompt;
  1525. int *possibles;
  1526. void (*handler) PARAMS ((Side *side, Map *map, int cancelled));
  1527. {
  1528.     int numtypes = 0, t;
  1529.  
  1530.     for_all_terrain_types(t) {
  1531.     if (possibles == NULL || possibles[t]) {
  1532.         map->tvec[numtypes] = t;
  1533.         map->tstr[numtypes] =
  1534.           (!empty_string(t_char(t)) ? t_char(t)[0] : (t - 'a'));
  1535.         ++numtypes;
  1536.     }
  1537.     }
  1538.     map->tstr[numtypes] = '\0';
  1539.     if (numtypes > 1) {
  1540.     sprintf(map->prompt, "%s [%s]", prompt, map->tstr);
  1541.     map->answer[0] = '\0';
  1542.     draw_prompt(side, map);
  1543.     map->modalhandler = handler;
  1544.     }
  1545.     return numtypes;
  1546. }
  1547.  
  1548. /* Do something with the char or terrain type that the player entered. */
  1549.  
  1550. int
  1551. grok_terrain_type(side, map, typep)
  1552. Side *side;
  1553. Map *map;
  1554. int *typep;
  1555. {
  1556.     int i;
  1557.  
  1558.     *typep = NONTTYPE;
  1559.     if (map->inpch == '?') {
  1560.     help_terrain_type(side, map);
  1561.     return FALSE;
  1562.     }
  1563.     i = iindex(map->inpch, map->tstr);
  1564.     if (i >= 0) {
  1565.     *typep = map->tvec[i];
  1566.     clear_prompt(side, map);
  1567.     return TRUE;
  1568.     } else {
  1569.     notify(side, "Must type a terrain type char or <esc>");
  1570.     return FALSE;
  1571.     }
  1572. }
  1573.  
  1574. static void
  1575. help_terrain_type(side, map)
  1576. Side *side;
  1577. Map *map;
  1578. {
  1579.     int i;
  1580.     char helpbuf[BUFSIZE];
  1581.  
  1582.     for (i = 0; map->tstr[i] != '\0'; ++i) {
  1583.     if (i % 4 == 0) {
  1584.         if (i > 0) {
  1585.         tprintf(helpbuf, "? for this help info"); 
  1586.         notify(side, "%s", helpbuf);
  1587.         }
  1588.         helpbuf[0] = '\0';
  1589.     }
  1590.     tprintf(helpbuf, "%c %s", map->tstr[i], t_type_name(map->tvec[i]));
  1591.     if (i > 0)
  1592.       tprintf(helpbuf, ", ");
  1593.     }
  1594. }
  1595.  
  1596. /* User is asked to pick a position on map.  This will iterate until the */
  1597. /* space bar designates the final position. */
  1598.  
  1599. /* (should change the cursor temporarily) */
  1600.  
  1601. void
  1602. ask_position(side, map, prompt, handler)
  1603. Side *side;
  1604. Map *map;
  1605. char *prompt;
  1606. void (*handler) PARAMS ((Side *side, Map *map, int cancelled));
  1607. {
  1608.     strcpy(map->prompt, prompt);
  1609.     map->answer[0] = '\0';
  1610.     draw_prompt(side, map);
  1611.     map->modalhandler = handler;
  1612. }
  1613.  
  1614. int
  1615. grok_position(side, map, xp, yp)
  1616. Side *side;
  1617. Map *map;
  1618. int *xp, *yp;
  1619. {
  1620.     
  1621.     if (in_area(map->curx, map->cury)) {
  1622.     *xp = map->curx;  *yp = map->cury;
  1623.     clear_prompt(side, map);
  1624.     return TRUE;
  1625.     } else {
  1626.     /* Make any possible usage attempts fail. */
  1627.     *xp = *yp = -1;
  1628.     return FALSE;
  1629.     }
  1630. }
  1631.  
  1632. /* Prompt for a yes/no answer with a settable default. */
  1633.  
  1634. void
  1635. ask_bool(side, map, question, dflt, handler)
  1636. Side *side;
  1637. Map *map;
  1638. char *question;
  1639. int dflt;
  1640. void (*handler) PARAMS ((Side *side, Map *map, int cancelled));
  1641. {
  1642.     sprintf(map->prompt, "%s [%s]", question, (dflt ? "yn" : "ny"));
  1643.     map->answer[0] = '\0';
  1644.     draw_prompt(side, map);
  1645.     map->tmpint = dflt;
  1646.     map->modalhandler = handler;
  1647. }
  1648.  
  1649. /* Figure out what the answer actually is, keeping the default in mind. */
  1650.  
  1651. int
  1652. grok_bool(side, map)
  1653. Side *side;
  1654. Map *map;
  1655. {
  1656.     int dflt = map->tmpint;
  1657.     char ch = map->inpch;
  1658.  
  1659.     if (dflt ? (lowercase(ch) == 'n') : (lowercase(ch) == 'y'))
  1660.       dflt = !dflt;
  1661.     clear_prompt(side, map);
  1662.     return dflt;
  1663. }
  1664.  
  1665. /* Read a string from the prompt window.  Deletion is allowed, and a
  1666.    text cursor (an underscore) is displayed. */
  1667.  
  1668. void
  1669. ask_string(side, map, prompt, dflt, handler)
  1670. Side *side;
  1671. Map *map;
  1672. char *prompt, *dflt;
  1673. void (*handler) PARAMS ((Side *side, Map *map, int cancelled));
  1674. {
  1675.     sprintf(map->prompt, "%s", prompt);
  1676.     /* Default must be non-NULL. */
  1677.     if (dflt == NULL)
  1678.       dflt = "";
  1679.     sprintf(map->answer, "%s", dflt);
  1680.     draw_prompt(side, map);
  1681.     map->modalhandler = handler;
  1682. }
  1683.  
  1684. /* Dig a character from the input and add it into the string.
  1685.    Keep returning FALSE until we get something, then make a copy
  1686.    of the result string and return TRUE. */
  1687.  
  1688. int
  1689. grok_string(side, map, strp)
  1690. Side *side;
  1691. Map *map;
  1692. char **strp;
  1693. {
  1694.     char ch = map->inpch;
  1695.     int len = strlen(map->answer);
  1696.  
  1697.     if (ch == '\r' || ch == '\n') {
  1698.     *strp = copy_string(map->answer);
  1699.     clear_prompt(side, map);
  1700.     return TRUE;
  1701.     } else {
  1702.     if (ch == BACKSPACE_CHAR || ch == DELETE_CHAR) {
  1703.         if (len > 0)
  1704.           --len;
  1705.     } else {
  1706.         map->answer[len++] = ch;
  1707.     }
  1708.     map->answer[len] = '\0';
  1709.     draw_prompt(side, map);
  1710.     return FALSE;
  1711.     }
  1712. }
  1713.  
  1714. void
  1715. ask_side(side, map, prompt, dflt, handler)
  1716. Side *side;
  1717. Map *map;
  1718. char *prompt;
  1719. Side *dflt;
  1720. void (*handler) PARAMS ((Side *side, Map *map, int cancel));
  1721. {
  1722.     sprintf(map->prompt, "%s", prompt);
  1723.     sprintf(map->answer, "%s", side_desig(dflt));
  1724.     draw_prompt(side, map);
  1725.     map->modalhandler = handler;
  1726. }
  1727.  
  1728. int
  1729. grok_side(side, map, side2p)
  1730. Side *side;
  1731. Map *map;
  1732. Side **side2p;
  1733. {
  1734.     *side2p = NULL;
  1735.     return TRUE;
  1736. }
  1737.  
  1738. /* Given a pixel in a map, describe what's there. */
  1739.  
  1740. static void
  1741. move_look(side, map, sx, sy)
  1742. Side *side;
  1743. Map *map;
  1744. int sx, sy;
  1745. {
  1746.     int nx, ny;
  1747.     Unit *unit;
  1748.  
  1749.     if (x_nearest_cell(side, map, sx, sy, &nx, &ny)) {
  1750.     if (inside_area(nx, ny)) {
  1751.         x_nearest_unit(side, map, sx, sy, &unit);
  1752.         if (unit != NULL
  1753.         && (side_controls_unit(side, unit) || map->seeall)) {
  1754.         set_current_unit(side, map, unit);
  1755.         } else {
  1756.         set_current_xy(side, map, nx, ny);
  1757.         }
  1758.     } else {
  1759.         beep(side);
  1760.     }
  1761.     }
  1762. }
  1763.  
  1764. /* Set the "current unit" of a map - the one being displayed, moved, etc. */
  1765.  
  1766. void
  1767. set_current_unit(side, map, unit)
  1768. Side *side;
  1769. Map *map;
  1770. Unit *unit;
  1771. {
  1772.     if (unit == map->curunit)
  1773.       return;
  1774.     clear_current(side, map);
  1775.     if (unit == NULL || (in_play(unit) && side_controls_unit(side, unit))) {
  1776.     map->curunit = unit;
  1777.     }
  1778.     /* Always shift the current position to where the unit is,
  1779.        whether or not it's one of ours. */
  1780.     if (unit != NULL) {
  1781.     map->curx = unit->x;  map->cury = unit->y;
  1782.     }
  1783.     draw_current(side, map);
  1784.     draw_map_info(side, map);
  1785.     put_on_screen(side, map, map->curx, map->cury);
  1786.     update_unit_controls(side, map);
  1787. }
  1788.  
  1789. /* Set the "current position" on the map. */
  1790.  
  1791. void
  1792. set_current_xy(side, map, x, y)
  1793. Side *side;
  1794. Map *map;
  1795. int x, y;
  1796. {
  1797.     if (x == map->curx && y == map->cury)
  1798.       return;
  1799.     clear_current(side, map);
  1800.     if (in_area(x, y)) {
  1801.     map->curx = x;  map->cury = y;
  1802.     }
  1803.     draw_current(side, map);
  1804.     draw_map_info(side, map);
  1805.     put_on_screen(side, map, map->curx, map->cury);
  1806.     update_unit_controls(side, map);
  1807. }
  1808.  
  1809. void
  1810. clear_current(side, map)
  1811. Side *side;
  1812. Map *map;
  1813. {
  1814.     int lastx = -1, lasty = -1;
  1815.     Unit *lastunit = NULL;
  1816.  
  1817.     if (map->curunit) {
  1818.     lastunit = map->curunit;
  1819.     } else if (in_area(map->curx, map->cury)) {
  1820.     lastx = map->curx;  lasty = map->cury;
  1821.     }
  1822.     map->curunit = NULL;
  1823.     map->curx = map->cury = -1;
  1824.     erase_current(side, map, lastx, lasty, lastunit);
  1825. }
  1826.  
  1827. /* Save the "cur" slots so we can move around without losing their
  1828.    values. */
  1829.  
  1830. void
  1831. save_cur(side, map)
  1832. Side *side;
  1833. Map *map;
  1834. {
  1835.     map->savedcurx = map->curx;  map->savedcury = map->cury;
  1836.     map->savedcurunit = map->curunit;
  1837. }
  1838.  
  1839. /* Restore the saved "cur" slots. */
  1840.  
  1841. void
  1842. restore_cur(side, map)
  1843. Side *side;
  1844. Map *map;
  1845. {
  1846.     map->curx = map->savedcurx;  map->cury = map->savedcury;
  1847.     map->curunit = map->savedcurunit;
  1848.     /* Erase the saved values. */
  1849.     map->savedcurx = map->savedcury = -1;
  1850.     map->savedcurunit = NULL;
  1851. }
  1852.  
  1853. /* If a given tool (mode) has a special cursor, use it. */
  1854.  
  1855. void
  1856. set_tool_cursor(side, map)
  1857. Side *side;
  1858. Map *map;
  1859. {
  1860.     int tool = map->curtool;
  1861.  
  1862.     if (side->ui->toolcursors[tool] == None) {
  1863.     fprintf(stderr, "No cursor!\n");
  1864.     abort();
  1865.     }
  1866.     XDefineCursor(side->ui->dpy, XtWindow(map->porthole),
  1867.           side->ui->toolcursors[tool]);
  1868. }
  1869.  
  1870. void
  1871. zoom_in_out(side, map, which)
  1872. Side *side;
  1873. Map *map;
  1874. int which;
  1875. {
  1876.     int newpower = map->vp->power + (which == ZOOM_OUT ? -1 : 1);
  1877.  
  1878.     if (newpower < 0)
  1879.       newpower = 0;
  1880.     if (newpower > NUMPOWERS - 1)
  1881.       newpower = NUMPOWERS - 1;
  1882.     if (newpower != map->vp->power) {
  1883.     set_map_power(side, map, newpower);
  1884.     x_center_on_focus(side, map);
  1885.     draw_map(side, map);
  1886.     update_controls(side, map);
  1887.     }
  1888. }
  1889.  
  1890. static void
  1891. mode_callback(w, client_data, call_data)
  1892. Widget w;
  1893. XtPointer client_data;
  1894. XtPointer call_data;
  1895. {
  1896.     int which = (int) client_data;
  1897.     Side *side;
  1898.     Map *map;
  1899.  
  1900.     if (!find_side_and_map_via_control(w, &side, &map))
  1901.       return;
  1902.  
  1903.     map->curtool = which;
  1904.     update_controls(side, map);
  1905.     set_tool_cursor(side, map);
  1906. }
  1907.  
  1908. static void
  1909. command_callback(w, client_data, call_data)
  1910. Widget w;
  1911. XtPointer client_data;
  1912. XtPointer call_data;
  1913. {
  1914.     int which = (int) client_data;
  1915.     Side *side;
  1916.     Map *map;
  1917.  
  1918.     if (!find_side_and_map_via_control(w, &side, &map))
  1919.       return;
  1920.  
  1921.     if (map->curunit == NULL) {
  1922.     beep(side);
  1923.     return;
  1924.     }
  1925.  
  1926.     map->frombutton = TRUE;
  1927.     switch (which) {
  1928.       case UNIT_MOVE:
  1929.     do_move_to(side, map);
  1930.     break;
  1931.       case UNIT_SHOOT:
  1932.     do_fire(side, map);
  1933.     break;
  1934.       case UNIT_BUILD:
  1935.     do_build(side, map);
  1936.     break;
  1937.       default:
  1938.     case_panic("not a valid command button", which);
  1939.     break;
  1940.     }
  1941.     /* Reset any prefix argument. */
  1942.     map->prefixarg = -1;
  1943. }
  1944.  
  1945. static void
  1946. view_flag_callback(w, client_data, call_data)
  1947. Widget w;
  1948. XtPointer client_data;
  1949. XtPointer call_data;
  1950. {
  1951.     int which = (int) client_data;
  1952.     int redraw = TRUE;
  1953.     Side *side;
  1954.     Map *map;
  1955.  
  1956.     if (!find_side_and_map_via_control(w, &side, &map))
  1957.       return;
  1958.  
  1959.     switch (which) {
  1960.       case SHOW_TERRAIN:
  1961.     map->drawterrain = !map->drawterrain;
  1962.     break;
  1963.       case SHOW_GRID:
  1964.     map->drawgrid = !map->drawgrid;
  1965.     break;
  1966.       case SHOW_UNITS:
  1967.     map->drawunits = !map->drawunits;
  1968.     break;
  1969.       case SHOW_NAMES:
  1970.     map->drawnames = !map->drawnames;
  1971.     break;
  1972.       case SHOW_PEOPLE:
  1973.     map->drawpeople = !map->drawpeople;
  1974.     break;
  1975.       case SHOW_ALL:
  1976.     if (!all_see_all)
  1977.       map->seeall = !map->seeall;
  1978.     else
  1979.       beep(side);
  1980.     break;
  1981.       case SHOW_ELEV:
  1982.     map->drawelevations = !map->drawelevations;
  1983.     break;
  1984.       case SHOW_MORE:
  1985.     if (!map->fullpanel)
  1986.       popup_ctrlpanel(side, map);
  1987.     else
  1988.       popdown_ctrlpanel(side, map);
  1989.     redraw = FALSE;
  1990.     break;
  1991.       default:
  1992.     break;
  1993.     }
  1994.     update_controls(side, map);
  1995.     /* Redraw the map to reflect the effect of the toggle. */
  1996.     if (redraw)
  1997.       draw_map(side, map);
  1998. }
  1999.  
  2000. static void
  2001. zoom_callback(w, client_data, call_data)
  2002. Widget w;
  2003. XtPointer client_data;
  2004. XtPointer call_data;
  2005. {
  2006.     int which = (int) client_data;
  2007.     Side *side;
  2008.     Map *map;
  2009.  
  2010.     if (!find_side_and_map_via_control(w, &side, &map))
  2011.       return;
  2012.  
  2013.     zoom_in_out(side, map, which);
  2014. }
  2015.  
  2016. static void
  2017. map_help_callback(w, client_data, call_data)
  2018. Widget w;
  2019. XtPointer client_data;
  2020. XtPointer call_data;
  2021. {
  2022.     Side *side;
  2023.     Map *map;
  2024.  
  2025.     if (!find_side_and_map_via_control(w, &side, &map))
  2026.       return;
  2027.  
  2028.     do_help(side, map);
  2029.     map->prefixarg = -1;
  2030. }
  2031.  
  2032. static void
  2033. unit_type_list_callback(w, client_data, call_data)
  2034. Widget w;
  2035. XtPointer client_data;
  2036. XtPointer call_data;
  2037. {
  2038.     int which = (int) client_data;
  2039.     Side *side;
  2040.     Map *map;
  2041.     void (*fn) PARAMS ((Side *sidex, Map *mapx, int cancelledx));
  2042.     
  2043.     if (!find_side_and_map_via_listform(w, &side, &map))
  2044.       return;
  2045.  
  2046.     /* Always ignore clicks on the List label. */
  2047.     if (which == 0)
  2048.       return;
  2049.     /* Call the modal handler if defined. */
  2050.     if (map->modalhandler) {
  2051.     /* Suppress any possible apparent keyboard input, and
  2052.        supply a type directly instead. */
  2053.         map->inpch = '\0';
  2054.     map->inptype = which - 1;
  2055.     fn = map->modalhandler;
  2056.     /* Remove the handler - will restore itself if needed. */
  2057.     map->modalhandler = NULL;
  2058.     (*fn)(side, map, FALSE);
  2059. #ifdef DESIGNERS
  2060.     } else if (side->designer && side->ui->design_shell) {
  2061.     side->ui->curutype = which - 1;
  2062.     update_curutype(side);
  2063.     notify(side, "will now be creating %s %s units",
  2064.            side_adjective(side_n(side->ui->curusidenumber)),
  2065.            u_type_name(side->ui->curutype));
  2066. #endif /* DESIGNERS */
  2067.     }
  2068. }
  2069.  
  2070. static void
  2071. panner_callback(w, client_data, call_data)
  2072. Widget w;
  2073. XtPointer client_data;
  2074. XtPointer call_data;
  2075. {
  2076.     Side *side;
  2077.     Map *map;
  2078.     XawPannerReport *prp = (XawPannerReport *) call_data;
  2079.     
  2080.     if (find_side_and_map_via_rightform(w, &side, &map)) {
  2081.     scroll_map_absolute(side, map, prp->slider_x + hexagon_adjust(map->vp), prp->slider_y);
  2082.     } else
  2083.       run_warning("Did not find side and map!\n");
  2084. }
  2085.  
  2086. static void
  2087. porthole_callback(w, client_data, call_data)
  2088. Widget w;
  2089. XtPointer client_data;
  2090. XtPointer call_data;
  2091. {
  2092.     Side *side;
  2093.     Map *map;
  2094.     XawPannerReport *prp = (XawPannerReport *) call_data;
  2095.     
  2096.     if (!find_side_and_map_via_mapform(w, &side, &map)) {
  2097.     run_warning("Did not find side and map!\n");
  2098.     return;
  2099.     }
  2100.  
  2101.     map->pxw = prp->slider_width;  map->pxh = prp->slider_height;
  2102.     set_view_size(map->vp, prp->slider_width, prp->slider_height);
  2103.     if (map->panner) {
  2104.     XtVaSetValues(map->panner,
  2105.               XtNsliderWidth, map->pxw,
  2106.               XtNsliderHeight, map->pxh,
  2107.               NULL);
  2108.     }
  2109. }
  2110.  
  2111. /* Put the map at an absolute position. */
  2112.  
  2113. void
  2114. scroll_map_absolute(side, map, sx, sy)
  2115. Side *side;
  2116. Map *map;
  2117. int sx, sy;
  2118. {
  2119.     int oldsx = map->vp->sx, oldsy = map->vp->sy;
  2120.  
  2121.     set_view_position(map->vp, sx, sy);
  2122.  
  2123.     /* Redraw if the scroll position actually changed. */
  2124.     if (oldsx != map->vp->sx || oldsy != map->vp->sy)
  2125.       draw_map(side, map);
  2126. }
  2127.  
  2128. #if 0
  2129. void
  2130. scroll_map_relative(side, map, sx, sy)
  2131. Side *side;
  2132. Map *map;
  2133. int sx, sy;
  2134. {
  2135.     int oldsx = map->vp->sx, oldsy = map->vp->sy;
  2136.  
  2137.     /* (genericize into a ui.c routine?) */
  2138.     /* if we're doing horiz scroll */
  2139.     if (sx != 0) {
  2140.     int lx = hexagon_adjust(map->vp);
  2141.     int hx = map->vp->totsw - lx;
  2142.  
  2143.     /* if the map is bigger than the view */
  2144.     if (map->vp->pxw < hx) {
  2145.         map->vp->sx += (sx * (map->vp->pxw - (2 * map->vp->hw)));
  2146.         if (map->vp->sx < lx) {
  2147.         map->vp->sx = (area.xwrap ? hx - (lx - map->vp->sx) : lx);
  2148.         } else if (map->vp->sx > hx) {
  2149.         map->vp->sx = (area.xwrap ? lx + (map->vp->sx - hx) : hx);
  2150.         }
  2151.     }
  2152.     }
  2153.     /* if we're doing vert scroll and there's even reason to */
  2154.     if (sy != 0 && map->vp->pxh < map->vp->totsh) {
  2155.         map->vp->sy += (sy * (map->vp->pxh - (2 * map->vp->hh)));
  2156.         if (map->vp->sy < 0) {
  2157.         map->vp->sy = 0;
  2158.         } else if (map->vp->sy > map->vp->totsh - map->vp->pxh) {
  2159.         map->vp->sy = map->vp->totsh - map->vp->pxh;
  2160.         }
  2161.     }
  2162.     /* Redraw if the scroll position actually changed. */
  2163.     if (oldsx != map->vp->sx || oldsy != map->vp->sy)
  2164.       draw_map(side, map);
  2165. }
  2166. #endif
  2167.  
  2168. static void
  2169. handle_map_click(side, map, sx, sy)
  2170. Side *side;
  2171. Map *map;
  2172. int sx, sy;
  2173. {
  2174.     int ax, ay;
  2175.     Unit *unit2;
  2176.  
  2177.     if (map == NULL) {
  2178.     beep(side);
  2179.     return;
  2180.     }
  2181.     if (!x_nearest_cell(side, map, sx, sy, &ax, &ay)) {
  2182.     beep(side);
  2183.     return;
  2184.     }
  2185.     /* Assume that last place clicked is a reasonable focus. */
  2186.     if (inside_area(ax, ay)) {
  2187.     set_view_focus(map->vp, ax, ay);
  2188.     }
  2189. #ifdef DESIGNERS
  2190.     if (side->designer && side->ui->curdesigntool != looktool) {
  2191.     handle_designer_map_click(side, map, sx, sy);
  2192.     return;
  2193.     }
  2194. #endif /* DESIGNERS */
  2195.     if (map->modalhandler) {
  2196.        void (*fn) PARAMS ((Side *sidex, Map *mapx, int cancelledx));
  2197.  
  2198.        fn = map->modalhandler;
  2199.        map->modalhandler = NULL;
  2200.        move_look(side, map, sx, sy);
  2201.        (*fn)(side, map, 0);
  2202.        return;
  2203.     }
  2204.  
  2205.     switch (map->curtool) {
  2206.       case looktool:
  2207.     move_look(side, map, sx, sy);
  2208.     break;
  2209.       case movetool:
  2210.       case unitmovetool:
  2211.     if (map->curunit && side_controls_unit(side, map->curunit)) {
  2212.         move_the_selected_unit(side, map, map->curunit, sx, sy);
  2213.     } else {
  2214.         move_look(side, map, sx, sy);
  2215.     }
  2216.     break;
  2217.       case unitshoottool:
  2218.     if (map->curunit && side_controls_unit(side, map->curunit)) {
  2219.         if (inside_area(ax, ay)) {
  2220.         if ((unit2 = unit_at(ax, ay)) != NULL) {
  2221.             if (map->curunit != unit2) {
  2222.             prep_fire_at_action(map->curunit, map->curunit,
  2223.                         unit2, -1);
  2224.             } else {
  2225.             /* don't attack ourselves */
  2226.             }
  2227.         } else {
  2228.             /* We're just shooting for the hell of it. */
  2229.             prep_fire_into_action(map->curunit, map->curunit,
  2230.                       ax, ay, 0, 0);
  2231.         }
  2232.         } else {
  2233.         beep(side);
  2234.         }
  2235.     } else {
  2236.     }
  2237.     break;
  2238.       case unitbuildtool:
  2239.     /* (what should this do?) */
  2240.     beep(side);
  2241.     break;
  2242.      default:
  2243.     /* error eventually */
  2244.     break;
  2245.     }
  2246. }
  2247.  
  2248. /* (should use advance_into_cell here) */
  2249.  
  2250. void
  2251. move_the_selected_unit(side, map, unit, sx, sy)
  2252. Side *side;
  2253. Map *map;
  2254. Unit *unit;
  2255. int sx, sy;
  2256. {
  2257.     int x, y;
  2258.     Unit *other = NULL;
  2259.  
  2260.     x_nearest_cell(side, map, sx, sy, &x, &y);
  2261.     x_nearest_unit(side, map, sx, sy, &other);
  2262. #ifdef DESIGNERS
  2263.     /* Designers use this function to push units around, bound only by the
  2264.        limits on occupancy. */
  2265.     if (side->designer) {
  2266.     designer_teleport(unit, x, y, other);
  2267.     return;
  2268.     }
  2269. #endif
  2270.     if (x != unit->x || y != unit->y) {
  2271.     /* we're outa here... leaving the cell that is */
  2272.  
  2273.     if (unit->act && unit->plan) { /* (should be more sophisticated?) */
  2274.  
  2275.         /* if it's far away, set up the task and boogie */
  2276.         if (distance(unit->x, unit->y, x, y) > 1) {
  2277.         DGprintf("Ordering %s to move to %d,%d\n",
  2278.              unit_desig(unit), x, y);
  2279.         set_move_to_task(unit, x, y);
  2280.         return;
  2281.         }
  2282.  
  2283.         /* If no one's home, try to move into it directly. */
  2284.         if (unit_at(x, y) == NULL) {
  2285.         if (can_occupy_cell(unit, x, y)
  2286.             && valid(check_move_action(unit, unit, x, y, 0))) {
  2287.             prep_move_action(unit, unit, x, y, 0);
  2288.         } else {
  2289.             beep(side);
  2290.         }
  2291.         return;
  2292.         }
  2293.  
  2294.         /* There are units at our desired destination. */
  2295.         if (other == NULL) {
  2296.         if (can_occupy_cell(unit, x, y)) {
  2297.             prep_move_action(unit, unit, x, y, 0);
  2298.         } else {
  2299.             beep(side);
  2300.         }
  2301.         } else if (other->side == unit->side) {
  2302.         /* One of ours, maybe get on it. */
  2303.         if (can_occupy(unit, other)) {
  2304.             prep_enter_action(unit, unit, other);
  2305.         } else if (can_occupy(other, unit)) {
  2306.             /* Have other unit do an enter action, then move. */
  2307.             /* (not quite right, move should happen after other unit
  2308.                is actually inside, in case it fills dest) */
  2309.             prep_enter_action(other, other, unit);
  2310.             set_move_to_task(unit, x, y);
  2311.         } else if (can_occupy_cell(unit, x, y)) {
  2312.             prep_move_action(unit, unit, x, y, 0);
  2313.         } else {
  2314.             beep(side);
  2315.         }
  2316.         } else {
  2317.         /* Somebody else's unit, try to victimize it. */
  2318.         if (valid(check_capture_action(unit, unit, other))) {
  2319.             prep_capture_action(unit, unit, other);
  2320.         } else if (valid(check_attack_action(unit, unit, other, 100))) {
  2321.             prep_attack_action(unit, unit, other, 100);
  2322.         } else {
  2323.             beep(side);
  2324.         }
  2325.         }
  2326.     }
  2327.     } else {
  2328.     /* moving around within the current cell */
  2329.     if (other != NULL && other != unit) {
  2330.         /* ok we're trying to hop onto another transport */
  2331.         if (can_occupy(unit, other)) {
  2332.         prep_enter_action(unit, unit, other);
  2333.         } else {
  2334.         /* maybe we should restack? */
  2335.         beep(side);
  2336.         }
  2337.     }
  2338.     }
  2339. }
  2340.  
  2341. void
  2342. destroy_map(side, map)
  2343. Side *side;
  2344. Map *map;
  2345. {
  2346.     /* find map and remove it from the list of maps */
  2347.     /* also put some other map in front if this one was the front one */
  2348. }
  2349.  
  2350. static void
  2351. panner_resize_handler (w, client_data, event, cont)
  2352. Widget w; 
  2353. XtPointer client_data; 
  2354. XEvent *event; 
  2355. Boolean *cont;
  2356. {
  2357.     Side *side;
  2358.     Map *map;
  2359.  
  2360.     if (event->type == ConfigureNotify) {
  2361.     for_all_sides(side) {
  2362.         if (active_display(side)) {
  2363.         if (XtDisplay(w) == side->ui->dpy) {
  2364.             for_all_maps(side, map) {
  2365.             if (w == map->panner) {
  2366.                 goto found;
  2367.             }
  2368.             }
  2369.         }
  2370.         }
  2371.     }
  2372.     return;
  2373.  
  2374.       found:
  2375.     draw_view_in_panner(side, map);
  2376.     } else {
  2377.     return;
  2378.     }
  2379. }
  2380.  
  2381. /* draw the world view in the background of the panner */
  2382.  
  2383. void
  2384. draw_view_in_panner(side, map)
  2385. Side *side;
  2386. Map *map;
  2387. {
  2388.     int x, y, l, t, mx, my, depth, w, h, b;
  2389.     Display *dpy = side->ui->dpy;
  2390.     Pixel pixel, bg=0, unknown;
  2391.     XImage *img;
  2392.     char *dp, *data;
  2393.     GC gc;
  2394.     extern int numdisplays;
  2395.  
  2396.     if (map->panner == NULL)
  2397.       return;
  2398.  
  2399.     unknown = XBlackPixel(dpy, side->ui->screen);
  2400.     depth = DefaultDepth(dpy, side->ui->screen);
  2401.     if (depth % 8)
  2402.       return;
  2403.  
  2404.    /* we need w, h, b as int, otherwise some compiler will miscompile
  2405.    mx = ((2 * (x - b) * area.width) / (w - 2 * b) - my) / 2;
  2406.    while XtVaGetValues needs them as Dimension */
  2407.     {
  2408.     Dimension wd = 0, hd = 0, bd = 0;
  2409.  
  2410.     XtVaGetValues(map->panner,
  2411.               XtNwidth, &wd,
  2412.               XtNheight, &hd,
  2413.               XtNinternalSpace, &bd,
  2414.               XtNbackground, &bg,
  2415.               NULL);
  2416.     w = wd;
  2417.     h = hd;
  2418.     b = bd;
  2419.     }
  2420.  
  2421.     /* b is the size of the internal border  */
  2422.     if (b <= 0 || h <= 2 * b || w <= 2 * b)
  2423.       return;
  2424.  
  2425.     /* Not using xmalloc because data will be freed within this function */
  2426.     data = (char *) malloc(w * h * depth / 8 * sizeof(char));
  2427.  
  2428.     dp = data;
  2429.     for (y = 0; y < h; y++) {
  2430.     if (y < b || y >= h - b) {
  2431.         my = -1;
  2432.     } else {
  2433.         my = ((h - 1 - b - y) * area.height) / (h - 2 * b);
  2434.     }
  2435.     for (x = 0; x < w; x++) {
  2436.         if (x < b || x >= w - b) {
  2437.         mx = -1;
  2438.         } else {
  2439.             /* get the right rounding for the division by 2 */
  2440.         mx = ((2 * (x - b) * area.width) / (w - 2 * b) - my) / 2;
  2441.         if (area.xwrap) {
  2442.             mx = wrapx(mx);
  2443.         } else {
  2444.             mx += area.height / 4;
  2445.         }
  2446.         }
  2447.         if (mx >= 0 && my >= 0 && in_area(mx, my)) {
  2448.         t = terrain_seen_at(side, mx, my);
  2449.         if (t == NONTTYPE) {
  2450.             pixel = unknown;
  2451.         } else {
  2452.             pixel = side->ui->cellcolor[t];
  2453.         }
  2454.         } else {
  2455.         pixel = bg;
  2456.         }
  2457.         for (l = depth - 8; l >= 0; l -= 8) {
  2458.         *dp = (pixel >> l) & 0xff;
  2459.         dp++;
  2460.         }
  2461.     }
  2462.     }
  2463.  
  2464.     img = XCreateImage(dpy, DefaultVisual(dpy, side->ui->screen), depth,
  2465.                ZPixmap, 0, data, w, h, 8, w * depth / 8);
  2466.     if (img == NULL) {
  2467.     free(data);
  2468.     return;
  2469.     }
  2470.     img->byte_order = MSBFirst;
  2471.     img->bitmap_bit_order = MSBFirst;
  2472.  
  2473.     /* clear the panner background before freeing the pixmap */    
  2474. #ifdef NO_PANNER_BGPIXMAP
  2475.     XSetWindowBackgroundPixmap(dpy, XtWindow(map->panner), None);
  2476. #else
  2477.     XtVaSetValues(map->panner, XtNbackgroundPixmap, None, NULL);
  2478. #endif
  2479.  
  2480.     if (map->panner_pix != None)
  2481.       XFreePixmap(dpy, map->panner_pix);
  2482.     map->panner_pix = XCreatePixmap(dpy, side->ui->rootwin, w, h, depth);
  2483.     if (!map->panner_pix || map->panner_pix == None) {
  2484.     XDestroyImage(img);
  2485.     /* XDestroyImage also frees data */
  2486.     return;
  2487.     }
  2488.  
  2489.     gc = XCreateGC(dpy, map->panner_pix, 0, NULL);
  2490.     XPutImage(dpy, map->panner_pix, gc, img, 0, 0, 0, 0, w, h);
  2491.     XFreeGC(dpy, gc);
  2492.     XDestroyImage(img);
  2493.     /* XDestroyImage also frees data */
  2494.  
  2495. #ifdef NO_PANNER_BGPIXMAP
  2496.     XSetWindowBackgroundPixmap(dpy, XtWindow(map->panner), map->panner_pix);
  2497.     /* at this point we force a redraw of the panner */
  2498.     XClearWindow(dpy, XtWindow(map->panner));
  2499. #else
  2500.     XtVaSetValues(map->panner, XtNbackgroundPixmap, map->panner_pix, NULL);
  2501. #endif
  2502. }
  2503.  
  2504. /* Popup control panel for maps, written by Massimo Campostrini. */
  2505.  
  2506. static void
  2507. popup_ctrlpanel(side, map)
  2508. Side *side;
  2509. Map *map;
  2510. {
  2511.     if (!map->ctrlpanel_shell)
  2512.       create_ctrlpanel(side, map);
  2513.     map->fullpanel = TRUE;
  2514.     XtPopup(map->ctrlpanel_shell, XtGrabNone);
  2515. }
  2516.  
  2517. int
  2518. find_side_and_map_via_ctrlpanel_form(w, sidep, mapp)
  2519. Widget w;
  2520. Side **sidep;
  2521. Map **mapp;
  2522. {
  2523.     Side *side;
  2524.     Map *map;
  2525.  
  2526.     for_all_sides(side) {
  2527.     if (active_display(side)) {
  2528.         if (XtDisplay(w) == side->ui->dpy) {
  2529.         for_all_maps(side, map) {
  2530.             if (map->ctrlpanel_form == w) {
  2531.             *sidep = side;
  2532.             *mapp = map;
  2533.             return TRUE;
  2534.             }
  2535.         }
  2536.         }
  2537.     }
  2538.     }
  2539.     return FALSE;
  2540. }
  2541.  
  2542. static void
  2543. create_ctrlpanel(side, map)
  2544. Side *side;
  2545. Map *map;
  2546. {
  2547.     Widget *buttons, label;
  2548.  
  2549.     map->ctrlpanel_shell =
  2550.       XtVaCreatePopupShell("controlPanel", topLevelShellWidgetClass, side->ui->shell,
  2551.                NULL);
  2552.  
  2553.     map->ctrlpanel_form =
  2554.       XtVaCreateManagedWidget("form", boxWidgetClass, map->ctrlpanel_shell,
  2555.                   NULL);
  2556.  
  2557.     buttons = (Widget *) xmalloc(numcontrols * sizeof (Widget));
  2558.     map->ctrlpanel_buttons = buttons;
  2559.  
  2560.     label =
  2561.       XtVaCreateManagedWidget("---what---", labelWidgetClass, map->ctrlpanel_form,
  2562.                   XtNborderWidth, 0,
  2563.                   NULL);
  2564.  
  2565.     map->ctrlpanel_buttons[SHOW_TERRAIN] =
  2566.       XtVaCreateManagedWidget("Terrain", toggleWidgetClass, map->ctrlpanel_form,
  2567.                   NULL);
  2568.     map->ctrlpanel_buttons[SHOW_GRID] =
  2569.       XtVaCreateManagedWidget("Grid", toggleWidgetClass, map->ctrlpanel_form,
  2570.                   NULL);
  2571.     map->ctrlpanel_buttons[SHOW_UNITS] =
  2572.       XtVaCreateManagedWidget("Units", toggleWidgetClass, map->ctrlpanel_form,
  2573.                   NULL);
  2574.     map->ctrlpanel_buttons[SHOW_NAMES] =
  2575.       XtVaCreateManagedWidget("Names", toggleWidgetClass, map->ctrlpanel_form,
  2576.                   NULL);
  2577.     map->ctrlpanel_buttons[SHOW_FEATURE_NAMES] =
  2578.       XtVaCreateManagedWidget("featureNames",  toggleWidgetClass, map->ctrlpanel_form,
  2579.                    XtNlabel, "Feature Names", NULL);
  2580.     map->ctrlpanel_buttons[SHOW_FEATURE_BOUNDARIES] =
  2581.       XtVaCreateManagedWidget("featureBoundaries",  toggleWidgetClass, map->ctrlpanel_form,
  2582.                    XtNlabel, "Feature Bound.", NULL);
  2583.     map->ctrlpanel_buttons[SHOW_PEOPLE] =
  2584.       XtVaCreateManagedWidget("People", toggleWidgetClass, map->ctrlpanel_form,
  2585.                   NULL);
  2586.     map->ctrlpanel_buttons[SHOW_ELEV] =
  2587.       XtVaCreateManagedWidget("Elevations", toggleWidgetClass, map->ctrlpanel_form,
  2588.                   NULL);
  2589.     map->ctrlpanel_buttons[SHOW_TEMP] =
  2590.       XtVaCreateManagedWidget("Temperatures", toggleWidgetClass, map->ctrlpanel_form,
  2591.                   NULL);
  2592.     map->ctrlpanel_buttons[SHOW_ALL] =
  2593.       XtVaCreateManagedWidget("See All", toggleWidgetClass, map->ctrlpanel_form,
  2594.                   XtNsensitive, !all_see_all,
  2595.                   NULL);
  2596.  
  2597.     label =
  2598.       XtVaCreateManagedWidget("--color---", labelWidgetClass, map->ctrlpanel_form,
  2599.                   XtNborderWidth, 0,
  2600.                   NULL);
  2601.  
  2602.     map->ctrlpanel_buttons[COLR_TERR] =
  2603.      XtVaCreateManagedWidget("Terrain", toggleWidgetClass, map->ctrlpanel_form,
  2604.                   XtNsensitive, !side->ui->monochrome,
  2605.                   NULL);
  2606.     map->ctrlpanel_buttons[COLR_UNITS] =
  2607.      XtVaCreateManagedWidget(" Units ", toggleWidgetClass, map->ctrlpanel_form,
  2608.                   XtNsensitive, !side->ui->monochrome,
  2609.                   NULL);
  2610.     map->ctrlpanel_buttons[COLR_EMBL] =
  2611.      XtVaCreateManagedWidget("Emblems", toggleWidgetClass, map->ctrlpanel_form,
  2612.                   XtNsensitive, !side->ui->monochrome,
  2613.                   NULL);
  2614.     label =
  2615.       XtVaCreateManagedWidget("---mono---", labelWidgetClass, map->ctrlpanel_form,
  2616.                   XtNborderWidth, 0,
  2617.                   NULL);
  2618.     map->ctrlpanel_buttons[MONO_REVERSE] =
  2619.       XtVaCreateManagedWidget("Reverse", toggleWidgetClass, map->ctrlpanel_form,
  2620.                   XtNsensitive, side->ui->monochrome,
  2621.                   NULL);
  2622.  
  2623.     label =
  2624.       XtVaCreateManagedWidget("----------", labelWidgetClass, map->ctrlpanel_form,
  2625.                   XtNborderWidth, 0,
  2626.                   NULL);
  2627.     label =
  2628.       XtVaCreateManagedWidget("Cancel", commandWidgetClass, map->ctrlpanel_form,
  2629.                   NULL);
  2630.     XtAddCallback(label, XtNcallback, ctrlpanel_cancel_callback, NULL);
  2631.     label =
  2632.       XtVaCreateManagedWidget("Revert", commandWidgetClass, map->ctrlpanel_form,
  2633.                   NULL);
  2634.     XtAddCallback(label, XtNcallback, ctrlpanel_revert_callback, NULL);
  2635.     label =
  2636.       XtVaCreateManagedWidget("Apply", commandWidgetClass, map->ctrlpanel_form,
  2637.                   NULL);
  2638.     XtAddCallback(label, XtNcallback, ctrlpanel_apply_callback, NULL);
  2639.     label =
  2640.       XtVaCreateManagedWidget("Done", commandWidgetClass, map->ctrlpanel_form,
  2641.                   NULL);
  2642.     XtAddCallback(label, XtNcallback, ctrlpanel_done_callback, NULL);
  2643. }
  2644.  
  2645. static void 
  2646. ctrlpanel_cancel_callback(w, client_data, call_data)
  2647. Widget w;
  2648. XtPointer client_data;
  2649. XtPointer call_data;
  2650. {
  2651.     Side *side;
  2652.     Map *map;
  2653.  
  2654.     if (!find_side_and_map_via_ctrlpanel_form(XtParent(w), &side, &map))
  2655.       return;
  2656.  
  2657.     popdown_ctrlpanel(side, map);
  2658. }
  2659.  
  2660. static void 
  2661. ctrlpanel_revert_callback(w, client_data, call_data)
  2662. Widget w;
  2663. XtPointer client_data;
  2664. XtPointer call_data;
  2665. {
  2666.     Side *side;
  2667.     Map *map;
  2668.  
  2669.     if (!find_side_and_map_via_ctrlpanel_form(XtParent(w), &side, &map))
  2670.       return;
  2671.  
  2672.     update_controls(side, map);
  2673. }
  2674.  
  2675. int get_toggle_state PARAMS ((Widget w));
  2676.  
  2677. int
  2678. get_toggle_state(w)
  2679. Widget w;
  2680. {
  2681.     Boolean rslt;
  2682.  
  2683.     XtVaGetValues(w, XtNstate, &rslt, NULL);
  2684.     return rslt; 
  2685. }
  2686.  
  2687. static int set_from_controls PARAMS ((Side *side, Map *map));
  2688.  
  2689. static int
  2690. set_from_controls(side, map)
  2691. Side *side;
  2692. Map *map;
  2693. {
  2694.     int newval, anychanged = FALSE;
  2695.  
  2696.     newval = get_toggle_state(map->ctrlpanel_buttons[SHOW_TERRAIN]);
  2697.     if (newval != map->drawterrain)
  2698.       anychanged = TRUE;
  2699.     map->drawterrain = newval;
  2700.     newval = get_toggle_state(map->ctrlpanel_buttons[SHOW_GRID]);
  2701.     if (newval != map->drawgrid)
  2702.       anychanged = TRUE;
  2703.     map->drawgrid = newval;
  2704.     newval = get_toggle_state(map->ctrlpanel_buttons[SHOW_UNITS]);
  2705.     if (newval != map->drawunits)
  2706.       anychanged = TRUE;
  2707.     map->drawunits = newval;
  2708.     newval = get_toggle_state(map->ctrlpanel_buttons[SHOW_NAMES]);
  2709.     if (newval != map->drawnames)
  2710.       anychanged = TRUE;
  2711.     map->drawnames = newval;
  2712.     newval = get_toggle_state(map->ctrlpanel_buttons[SHOW_FEATURE_NAMES]);
  2713.     if (newval != map->drawfeaturenames)
  2714.       anychanged = TRUE;
  2715.     map->drawfeaturenames = newval;
  2716.     newval = get_toggle_state(map->ctrlpanel_buttons[SHOW_FEATURE_BOUNDARIES]);
  2717.     if (newval != map->drawfeatureboundaries)
  2718.       anychanged = TRUE;
  2719.     map->drawfeatureboundaries = newval;
  2720.     newval = get_toggle_state(map->ctrlpanel_buttons[SHOW_PEOPLE]);
  2721.     if (newval != map->drawpeople)
  2722.       anychanged = TRUE;
  2723.     map->drawpeople = newval;
  2724.     newval = get_toggle_state(map->ctrlpanel_buttons[SHOW_ELEV]);
  2725.     if (newval != map->drawelevations)
  2726.       anychanged = TRUE;
  2727.     map->drawelevations = newval;
  2728.     newval = get_toggle_state(map->ctrlpanel_buttons[SHOW_TEMP]);
  2729.     if (newval != map->drawtemp)
  2730.       anychanged = TRUE;
  2731.     map->drawtemp = newval;
  2732.     newval = get_toggle_state(map->ctrlpanel_buttons[SHOW_ALL]);
  2733.     if (newval != map->seeall)
  2734.       anychanged = TRUE;
  2735.     map->seeall = newval;
  2736.     /* (shouldn't be affecting all maps without redrawing all...) */
  2737.     newval = get_toggle_state(map->ctrlpanel_buttons[COLR_TERR]);
  2738.     if (newval != side->ui->dflt_color_terr_images)
  2739.       anychanged = TRUE;
  2740.     side->ui->dflt_color_terr_images = newval;
  2741.     newval = get_toggle_state(map->ctrlpanel_buttons[COLR_UNITS]);
  2742.     if (newval != side->ui->dflt_color_unit_images)
  2743.       anychanged = TRUE;
  2744.     side->ui->dflt_color_unit_images = newval;
  2745.     newval = get_toggle_state(map->ctrlpanel_buttons[COLR_EMBL]);
  2746.     if (newval != side->ui->dflt_color_embl_images)
  2747.       anychanged = TRUE;
  2748.     side->ui->dflt_color_embl_images = newval;
  2749.     return anychanged;
  2750. }
  2751.  
  2752. static void 
  2753. ctrlpanel_apply_callback(w, client_data, call_data)
  2754. Widget w;
  2755. XtPointer client_data;
  2756. XtPointer call_data;
  2757. {
  2758.     Side *side;
  2759.     Map *map;
  2760.     int changed;
  2761.  
  2762.     if (!find_side_and_map_via_ctrlpanel_form(XtParent(w), &side, &map))
  2763.       return;
  2764.  
  2765.     changed = set_from_controls(side, map);
  2766.     if (changed)
  2767.       draw_map(side, map);
  2768. }
  2769.  
  2770. static void 
  2771. ctrlpanel_done_callback(w, client_data, call_data)
  2772. Widget w;
  2773. XtPointer client_data;
  2774. XtPointer call_data;
  2775. {
  2776.     Side *side;
  2777.     Map *map;
  2778.     int changed;
  2779.  
  2780.     if (!find_side_and_map_via_ctrlpanel_form(XtParent(w), &side, &map))
  2781.       return;
  2782.  
  2783.     changed = set_from_controls(side, map);
  2784.     popdown_ctrlpanel(side, map);
  2785.     if (changed)
  2786.       draw_map(side, map);
  2787. }
  2788.  
  2789. static void
  2790. popdown_ctrlpanel(side, map)
  2791. Side *side;
  2792. Map *map;
  2793. {
  2794.     map->fullpanel = FALSE;
  2795.     if (map->ctrlpanel_shell)
  2796.       XtPopdown(map->ctrlpanel_shell);
  2797. }
  2798.  
  2799. void set_control_state PARAMS ((Side *side, Map *map, int, int, int));
  2800.  
  2801. void
  2802. update_controls(side, map)
  2803. Side *side;
  2804. Map *map;
  2805. {
  2806.     set_control_state(side, map, LOOK, TRUE, (map->curtool == looktool));
  2807.     set_control_state(side, map, MOVE, TRUE, (map->curtool == movetool));
  2808.     update_unit_controls(side, map);
  2809.     set_control_state(side, map, SHOW_TERRAIN, TRUE, map->drawterrain);
  2810.     set_control_state(side, map, SHOW_GRID, TRUE, map->drawgrid);
  2811.     set_control_state(side, map, SHOW_UNITS, TRUE, map->drawunits);
  2812.     set_control_state(side, map, SHOW_NAMES, TRUE, map->drawnames);
  2813.     set_control_state(side, map, SHOW_FEATURE_NAMES, TRUE, map->drawfeaturenames);
  2814.     set_control_state(side, map, SHOW_FEATURE_BOUNDARIES, TRUE, map->drawfeatureboundaries);
  2815.     set_control_state(side, map, SHOW_PEOPLE, people_sides_defined(), map->drawpeople);
  2816.     set_control_state(side, map, SHOW_ELEV, elevations_defined(), map->drawelevations);
  2817.     set_control_state(side, map, SHOW_TEMP, temperatures_defined(), map->drawtemp);
  2818.     set_control_state(side, map, SHOW_ALL, side->may_set_see_all, map->seeall);
  2819.     set_control_state(side, map, SHOW_MORE, TRUE, map->fullpanel);
  2820.     set_control_state(side, map, ZOOM_OUT, (map->vp->power > 0), FALSE);
  2821.     set_control_state(side, map, ZOOM_IN,  (map->vp->power < NUMPOWERS - 1), FALSE);
  2822.     set_control_state(side, map, COLR_TERR, !side->ui->monochrome,
  2823.               side->ui->dflt_color_terr_images);
  2824.     set_control_state(side, map, COLR_UNITS, !side->ui->monochrome,
  2825.               side->ui->dflt_color_unit_images);
  2826.     set_control_state(side, map, COLR_EMBL, !side->ui->monochrome,
  2827.               side->ui->dflt_color_embl_images);
  2828. }
  2829.  
  2830. void
  2831. update_unit_controls(side, map)
  2832. Side *side;
  2833. Map *map;
  2834. {
  2835.     set_control_state(side, map, UNIT_MOVE,
  2836.               (map->curunit != NULL && can_move_at_all(map->curunit)), FALSE);
  2837.     set_control_state(side, map, UNIT_SHOOT,
  2838.               (map->curunit != NULL && can_fire(map->curunit)), FALSE);
  2839.     set_control_state(side, map, UNIT_BUILD,
  2840.                   (map->curunit != NULL && can_build(map->curunit)), FALSE);
  2841. }
  2842.  
  2843. void
  2844. set_control_state(side, map, contype, active, state)
  2845. Side *side;
  2846. Map *map;
  2847. int contype, active, state;
  2848. {
  2849.     nargs = 0;
  2850.     XtSetArg(tmpargs[nargs], XtNsensitive, (Boolean) active);  nargs++;
  2851.     /* Note that the XtNstate property can be set for command widgets,
  2852.        and will simply have no effect. */
  2853.     XtSetArg(tmpargs[nargs], XtNstate, (Boolean) state);  nargs++;
  2854.     if (map->controls[contype])
  2855.       XtSetValues(map->controls[contype], tmpargs, nargs);
  2856.     if (map->fullpanel && map->ctrlpanel_shell && map->ctrlpanel_buttons[contype])
  2857.       XtSetValues(map->ctrlpanel_buttons[contype], tmpargs, nargs);
  2858. }
  2859.  
  2860. void
  2861. set_message_area(map, msg)
  2862. Map *map;
  2863. char *msg;
  2864. {
  2865.     /* Can we? */
  2866.     if (!map->msgarea)
  2867.       return;
  2868.     nargs = 0;
  2869.     XtSetArg(tmpargs[nargs], XtNlabel, msg);    nargs++;
  2870.     XtSetValues(map->msgarea, tmpargs, nargs);
  2871. }
  2872.  
  2873. /* Prompt line drawing. */
  2874.  
  2875. void
  2876. draw_prompt(side, map)
  2877. Side *side;
  2878. Map *map;
  2879. {
  2880.     char tmpbuf[BUFSIZE];
  2881.  
  2882.     if (map == NULL)
  2883.       return;
  2884.     sprintf(tmpbuf, "%s %s", map->prompt, map->answer);
  2885.     nargs = 0;
  2886.     XtSetArg(tmpargs[nargs], XtNlabel, tmpbuf);  nargs++;
  2887.     XtSetValues(map->promptlabel, tmpargs, nargs);
  2888.     /* should change the cursor so that the user knows that an
  2889.        answer is expected. */
  2890. }
  2891.  
  2892. void
  2893. clear_prompt(side, map)
  2894. Side *side;
  2895. Map *map;
  2896. {
  2897.     if (map == NULL)
  2898.       return;
  2899.     map->prompt[0] = '\0';
  2900.     map->answer[0] = '\0';
  2901.     nargs = 0;
  2902.     XtSetArg(tmpargs[nargs], XtNlabel, " ");  nargs++;
  2903.     XtSetValues(map->promptlabel, tmpargs, nargs);
  2904. }
  2905.  
  2906.  
  2907. void
  2908. draw_game_state(side, map)
  2909. Side *side;
  2910. Map *map;
  2911. {
  2912.     char tmpbuf[BUFSIZE];
  2913.     extern char *curseasonname;
  2914.     extern int paused;
  2915.  
  2916.     if (map == NULL || map->gamedate == None)
  2917.       return;
  2918.     sprintf(tmpbuf, "%s", absolute_date_string(g_turn()));
  2919.     if (curseasonname != NULL)
  2920.       tprintf(tmpbuf, " (%s)", curseasonname);
  2921.     if (paused)
  2922.       tprintf(tmpbuf, " PAUSED");
  2923.     XtVaSetValues(map->gamedate, XtNlabel, tmpbuf, NULL);
  2924.     draw_game_clocks(side, map);
  2925.     /* update the Info window; Massimo: */ draw_map_info(side, map);
  2926. }
  2927.  
  2928. /* This displays the gamewide clock(s) if defined. */
  2929.  
  2930. void
  2931. draw_game_clocks(side, map)
  2932. Side *side;
  2933. Map *map;
  2934. {
  2935.     int elapsed;
  2936.     time_t now;
  2937.     char tmpbuf[BUFSIZE];
  2938.  
  2939.     if (map == NULL || map->gameclock == None)
  2940.       return;
  2941.     nargs = 0;
  2942.     XtSetArg(tmpargs[nargs], XtNlabel, " ");  nargs++;
  2943.     XtSetValues(map->gameclock, tmpargs, nargs);
  2944.     if (g_rt_for_game() > 0) {
  2945.     time(&now);
  2946.     elapsed = idifftime(now, game_start_in_real_time);
  2947.     time_desc(tmpbuf, max(0, g_rt_for_game() - elapsed), g_rt_for_game());
  2948.     nargs = 0;
  2949.     XtSetArg(tmpargs[nargs], XtNlabel, tmpbuf);  nargs++;
  2950.     XtSetValues(map->gameclock, tmpargs, nargs);
  2951.     }
  2952.     /* (should not overwrite prev, if both defined) */
  2953.     if (g_rt_per_turn() > 0) {
  2954.     time(&now);
  2955.     elapsed = idifftime(now, turn_play_start_in_real_time);
  2956.     time_desc(tmpbuf, max(0, g_rt_per_turn() - elapsed), g_rt_per_turn());
  2957.     nargs = 0;
  2958.     XtSetArg(tmpargs[nargs], XtNlabel, tmpbuf);  nargs++;
  2959.     XtSetValues(map->gameclock, tmpargs, nargs);
  2960.     }
  2961. }
  2962.  
  2963. /* Alter the numbers for a single type of unit.  Should be called right
  2964.    after any changes.  Formatted to look nice, but kind of messy to set
  2965.    up correctly; display should not jump back and forth as the numbers
  2966.    change in size. */
  2967.  
  2968. void
  2969. update_unit_type_list(side, map, u)
  2970. Side *side;
  2971. Map *map;
  2972. int u;
  2973. {
  2974.     int num, changed = FALSE;
  2975.  
  2976.     if (!between(0, u, numutypes))
  2977.       return;
  2978.     spbuf[0] = '\0';
  2979.     /* Our unit total (right-justified) */
  2980.     num = num_units_in_play(side, u);
  2981.     if (num != map->last_num_in_play[u]) {
  2982.     map->last_num_in_play[u] = num;
  2983.     changed = TRUE;
  2984.     }
  2985.     if (num > 0)    {
  2986.     sprintf(tmpbuf, "%4d", num);
  2987.     } else {
  2988.     sprintf(tmpbuf, "    ");
  2989.     }
  2990.     strcat(spbuf, tmpbuf);
  2991.     /* Our units under construction (left-justified) */
  2992.     num = num_units_incomplete(side, u);
  2993.     if (num != map->last_num_incomplete[u]) {
  2994.     map->last_num_incomplete[u] = num;
  2995.     changed = TRUE;
  2996.     }
  2997.     if (num > 0) {
  2998.     sprintf(tmpbuf, "(%d)", num);
  2999.     } else {
  3000.     sprintf(tmpbuf, "    ");
  3001.     }
  3002.     strcat(spbuf, tmpbuf);
  3003.     /* (should do other columns too) */
  3004.     if (changed)
  3005.       XtVaSetValues(map->list_buttons[u + 1], XtNlabel, spbuf, NULL);
  3006. }
  3007.  
  3008. void
  3009. place_legends(side)
  3010. Side *side;
  3011. {
  3012.     int nf = side->ui->numfeatures;
  3013.  
  3014.     if (!features_defined() || nf <= 0)
  3015.       return;
  3016.     if (side->ui->legends == NULL)
  3017.       side->ui->legends = (Legend *) xmalloc((nf + 1) * sizeof(Legend));
  3018.     place_feature_legends(side->ui->legends, nf, side, 0, 1);
  3019. }
  3020.